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>
16 #if __has_include(<filesystem>)
18 namespace fs
= std::filesystem
;
20 #include <experimental/filesystem>
21 namespace fs
= std::experimental::filesystem
;
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"
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.
44 #define dout_subsys ceph_subsys_monc
52 using std::ostringstream
;
55 using std::string_view
;
58 using ceph::bufferlist
;
61 using ceph::Formatter
;
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"
67 ",$programdata/ceph/$cluster.conf"
72 #define STRINGIFY(x) _STR(x)
74 const char *ceph_conf_level_name(int 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 "???";
88 int ceph_resolve_file_search(const std::string
& filename_list
,
92 get_str_list(filename_list
, ";,", ls
);
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
);
110 static int conf_stringify(const Option::value_t
& v
, string
*out
)
112 if (boost::get
<boost::blank
>(&v
)) {
115 *out
= Option::to_str(v
);
119 md_config_t::md_config_t(ConfigValues
& values
,
120 const ConfigTracker
& tracker
,
122 : is_daemon(is_daemon
)
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
<< "'"
133 schema
.emplace(i
.name
, i
);
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.");
150 opt
.set_validator([](std::string
*value
, std::string
*error_message
) {
152 int r
= sscanf(value
->c_str(), "%d/%d", &m
, &n
);
154 if (m
< 0 || m
> 99) {
155 *error_message
= "value must be in range [0, 99]";
159 if (n
< 0 || n
> 99) {
160 *error_message
= "value must be in range [0, 99]";
166 *value
= stringify(m
) + "/" + stringify(n
);
169 *error_message
= "value must take the form N or N/M, where N and M are integers";
175 for (auto& opt
: subsys_options
) {
176 schema
.emplace(opt
.name
, opt
);
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.
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"
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
;
204 default_val
= opt
.value
;
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
;
211 if (opt
.pre_validate(&val
, &err
) != 0) {
212 std::cerr
<< "Default value " << opt
.name
<< "=" << *def_str
<< " is "
213 "invalid: " << err
<< std::endl
;
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.
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
);
228 // Copy out values (defaults) into any legacy (C struct member) fields
229 update_legacy_vals(values
);
232 md_config_t::~md_config_t()
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.
240 void md_config_t::validate_schema()
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
;
253 for (const auto &i
: legacy_values
) {
254 if (schema
.count(i
.first
) == 0) {
255 std::cerr
<< "Schema is missing legacy field '" << i
.first
<< "'"
262 const Option
*md_config_t::find_option(const std::string_view name
) const
264 auto p
= schema
.find(name
);
265 if (p
!= schema
.end()) {
271 void md_config_t::set_val_default(ConfigValues
& values
,
272 const ConfigTracker
& tracker
,
273 const string_view name
, const std::string
& val
)
275 const Option
*o
= find_option(name
);
278 int r
= _set_val(values
, tracker
, val
, *o
, CONF_DEFAULT
, &err
);
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
)
288 ignored_mon_values
.clear();
291 ldout(cct
, 4) << __func__
<< " no callback set" << dendl
;
296 if (config_cb(i
.first
, i
.second
)) {
297 ldout(cct
, 4) << __func__
<< " callback consumed " << i
.first
<< dendl
;
300 ldout(cct
, 4) << __func__
<< " callback ignored " << i
.first
<< dendl
;
302 const Option
*o
= find_option(i
.first
);
304 ldout(cct
,10) << __func__
<< " " << i
.first
<< " = " << i
.second
305 << " (unrecognized option)" << dendl
;
308 if (o
->has_flag(Option::FLAG_NO_MON_UPDATE
)) {
309 ignored_mon_values
.emplace(i
);
313 int r
= _set_val(values
, tracker
, i
.second
, *o
, CONF_MON
, &err
);
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
;
328 values
.for_each([&] (auto name
, auto configs
) {
329 auto config
= configs
.find(CONF_MON
);
330 if (config
== configs
.end()) {
333 if (kv
.find(name
) != kv
.end()) {
336 ldout(cct
,10) << __func__
<< " " << name
337 << " cleared (was " << Option::to_str(config
->second
) << ")"
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
);
347 update_legacy_vals(values
);
351 int md_config_t::parse_config_files(ConfigValues
& values
,
352 const ConfigTracker
& tracker
,
353 const char *conf_files_str
,
354 std::ostream
*warnings
,
357 if (safe_to_start_threads
)
360 if (values
.cluster
.empty() && !conf_files_str
) {
361 values
.cluster
= get_cluster_name(nullptr);
364 for (auto& fn
: get_conffile_paths(values
, conf_files_str
, warnings
, flags
)) {
367 if (bl
.read_file(fn
.c_str(), &error
)) {
372 int ret
= parse_buffer(values
, tracker
, bl
.c_str(), bl
.length(), &oss
);
378 parse_error
= oss
.str();
379 if (ret
!= -ENOENT
) {
383 // it must have been all ENOENTs, that's the only way we got here
384 if (conf_path
.empty()) {
387 if (values
.cluster
.empty()) {
388 values
.cluster
= get_cluster_name(conf_path
.c_str());
390 update_legacy_vals(values
);
395 md_config_t::parse_buffer(ConfigValues
& values
,
396 const ConfigTracker
& tracker
,
397 const char* buf
, size_t len
,
398 std::ostream
* warnings
)
400 if (!cf
.parse_buffer(string_view
{buf
, len
}, warnings
)) {
403 const auto my_sections
= get_my_sections(values
);
404 for (const auto &i
: schema
) {
405 const auto &opt
= i
.second
;
407 if (_get_val_from_conf_file(my_sections
, opt
.name
, val
)) {
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
<< ")";
422 cf
.check_old_style_section_names({"mds", "mon", "osd"}, cerr
);
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
,
432 if (!conf_files_str
) {
433 const char *c
= getenv("CEPH_CONF");
437 if (flags
& CINIT_FLAG_NO_DEFAULT_CONFIG_FILE
)
439 conf_files_str
= CEPH_CONF_FILE_DEFAULT
;
443 std::list
<std::string
> paths
;
444 get_str_list(conf_files_str
, ";,", paths
);
445 for (auto i
= paths
.begin(); i
!= paths
.end(); ) {
447 if (path
.find("$data_dir") != path
.npos
&&
448 data_dir_option
.empty()) {
449 // useless $data_dir item, skip
452 early_expand_meta(values
, path
, warnings
);
459 std::string
md_config_t::get_cluster_name(const char* 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();
467 // If the configuration file does not follow $cluster.conf
468 // convention, we do the last try and assign the cluster to
473 // set the cluster name to 'ceph' when configuration file is not specified.
478 void md_config_t::parse_env(unsigned entity_type
,
479 ConfigValues
& values
,
480 const ConfigTracker
& tracker
,
481 const char *args_var
)
483 if (safe_to_start_threads
)
486 args_var
= "CEPH_ARGS";
488 if (auto s
= getenv("CEPH_KEYRING"); s
) {
490 _set_val(values
, tracker
, s
, *find_option("keyring"), CONF_ENV
, &err
);
492 if (auto dir
= getenv("CEPH_LIB"); dir
) {
493 for (auto name
: { "erasure_code_dir", "plugin_dir", "osd_class_dir" }) {
495 const Option
*o
= find_option(name
);
497 _set_val(values
, tracker
, dir
, *o
, CONF_ENV
, &err
);
501 // Apply pod memory limits:
503 // There are two types of resource requests: `limits` and `requests`.
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
512 // If POD_MEMORY_REQUEST is present, we use it as the target.
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.
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.)
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
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
534 // The support in Rook is already there for applying the limits as seen in
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
542 uint64_t pod_limit
= 0, pod_request
= 0;
543 if (auto pod_lim
= getenv("POD_MEMORY_LIMIT"); pod_lim
) {
545 uint64_t v
= atoll(pod_lim
);
547 switch (entity_type
) {
548 case CEPH_ENTITY_TYPE_OSD
:
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
));
563 if (auto pod_req
= getenv("POD_MEMORY_REQUEST"); pod_req
) {
564 if (uint64_t v
= atoll(pod_req
); v
) {
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
);
578 switch (entity_type
) {
579 case CEPH_ENTITY_TYPE_OSD
:
580 _set_val(values
, tracker
, stringify(pod_request
),
581 *find_option("osd_memory_target"),
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
);
594 void md_config_t::show_config(const ConfigValues
& values
,
595 std::ostream
& out
) const
597 _show_config(values
, &out
, nullptr);
600 void md_config_t::show_config(const ConfigValues
& values
,
603 _show_config(values
, nullptr, f
);
606 void md_config_t::config_options(Formatter
*f
) const
608 f
->open_array_section("options");
609 for (const auto& i
: schema
) {
610 f
->dump_object("option", i
.second
);
615 void md_config_t::_show_config(const ConfigValues
& values
,
616 std::ostream
*out
, Formatter
*f
) const
619 *out
<< "name = " << values
.name
<< std::endl
;
620 *out
<< "cluster = " << values
.cluster
<< std::endl
;
623 f
->dump_string("name", stringify(values
.name
));
624 f
->dump_string("cluster", values
.cluster
);
626 for (const auto& i
: schema
) {
627 const Option
&opt
= i
.second
;
629 conf_stringify(_get_val(values
, opt
), &val
);
631 *out
<< opt
.name
<< " = " << val
<< std::endl
;
634 f
->dump_string(opt
.name
.c_str(), val
);
639 int md_config_t::parse_argv(ConfigValues
& values
,
640 const ConfigTracker
& tracker
,
641 std::vector
<const char*>& args
, int level
)
643 if (safe_to_start_threads
) {
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.
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. */
658 else if (ceph_argparse_flag(args
, i
, "--show_conf", (char*)NULL
)) {
659 cerr
<< cf
<< std::endl
;
662 else if (ceph_argparse_flag(args
, i
, "--show_config", (char*)NULL
)) {
663 do_show_config
= true;
665 else if (ceph_argparse_witharg(args
, i
, &val
, "--show_config_value", (char*)NULL
)) {
666 do_show_config_value
= val
;
668 else if (ceph_argparse_flag(args
, i
, "--no-mon-config", (char*)NULL
)) {
669 values
.no_mon_config
= true;
671 else if (ceph_argparse_flag(args
, i
, "--mon-config", (char*)NULL
)) {
672 values
.no_mon_config
= false;
674 else if (ceph_argparse_flag(args
, i
, "--foreground", "-f", (char*)NULL
)) {
675 set_val_or_die(values
, tracker
, "daemonize", "false");
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");
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
688 else if (ceph_argparse_witharg(args
, i
, &val
, "--monmap", "-M", (char*)NULL
)) {
689 set_val_or_die(values
, tracker
, "monmap", val
.c_str());
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());
694 else if (ceph_argparse_witharg(args
, i
, &val
, "--bind", (char*)NULL
)) {
695 set_val_or_die(values
, tracker
, "public_addr", val
.c_str());
697 else if (ceph_argparse_witharg(args
, i
, &val
, "--keyfile", "-K", (char*)NULL
)) {
702 r
= bl
.read_fd(STDIN_FILENO
, 1024);
704 r
= bl
.read_file(val
.c_str(), &err
);
707 string
k(bl
.c_str(), bl
.length());
708 set_val_or_die(values
, tracker
, "key", k
.c_str());
711 else if (ceph_argparse_witharg(args
, i
, &val
, "--keyring", "-k", (char*)NULL
)) {
712 set_val_or_die(values
, tracker
, "keyring", val
.c_str());
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());
718 int r
= parse_option(values
, tracker
, args
, i
, NULL
, level
);
724 // meta expands could have modified anything. Copy it all out again.
725 update_legacy_vals(values
);
729 void md_config_t::do_argv_commands(const ConfigValues
& values
) const
732 if (do_show_config
) {
733 _show_config(values
, &cout
, NULL
);
737 if (do_show_config_value
.size()) {
739 int r
= conf_stringify(_get_val(values
, do_show_config_value
, 0, &cerr
),
743 std::cerr
<< "failed to get config option '"
744 << do_show_config_value
<< "': option not found" << std::endl
;
746 std::cerr
<< "failed to get config option '"
747 << do_show_config_value
<< "': " << cpp_strerror(r
)
751 std::cout
<< val
<< std::endl
;
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
,
767 std::string option_name
;
768 std::string error_message
;
770 for (const auto& opt_iter
: schema
) {
771 const Option
&opt
= opt_iter
.second
;
773 std::string
as_option("--");
774 as_option
+= opt
.name
;
775 option_name
= opt
.name
;
776 if (ceph_argparse_witharg(
778 string(string("--default-") + opt
.name
).c_str(), (char*)NULL
)) {
779 if (!err
.str().empty()) {
780 error_message
= err
.str();
784 ret
= _set_val(values
, tracker
, val
, opt
, CONF_DEFAULT
, &error_message
);
786 } else if (opt
.type
== Option::TYPE_BOOL
) {
788 if (ceph_argparse_binary_flag(args
, i
, &res
, oss
, as_option
.c_str(),
791 ret
= _set_val(values
, tracker
, "false", opt
, level
, &error_message
);
793 ret
= _set_val(values
, tracker
, "true", opt
, level
, &error_message
);
798 std::string
no("--no-");
800 if (ceph_argparse_flag(args
, i
, no
.c_str(), (char*)NULL
)) {
801 ret
= _set_val(values
, tracker
, "false", opt
, level
, &error_message
);
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();
812 ret
= _set_val(values
, tracker
, val
, opt
, level
, &error_message
);
818 if (ret
< 0 || !error_message
.empty()) {
819 ceph_assert(!option_name
.empty());
821 *oss
<< "Parse error setting " << option_name
<< " to '"
822 << val
<< "' using injectargs";
823 if (!error_message
.empty()) {
824 *oss
<< " (" << error_message
<< ")";
828 cerr
<< "parse error setting '" << option_name
<< "' to '"
830 if (!error_message
.empty()) {
831 cerr
<< " (" << error_message
<< ")";
833 cerr
<< "\n" << std::endl
;
837 if (o
== schema
.size()) {
841 return ret
>= 0 ? 0 : ret
;
844 int md_config_t::parse_injectargs(ConfigValues
& values
,
845 const ConfigTracker
& tracker
,
846 std::vector
<const char*>& args
,
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
);
858 void md_config_t::set_safe_to_start_threads()
860 safe_to_start_threads
= true;
863 void md_config_t::_clear_safe_to_start_threads()
865 safe_to_start_threads
= false;
868 int md_config_t::injectargs(ConfigValues
& values
,
869 const ConfigTracker
& tracker
,
870 const std::string
& s
, std::ostream
*oss
)
873 char b
[s
.length()+1];
874 strcpy(b
, s
.c_str());
875 std::vector
<const char*> nargs
;
879 while (*p
&& *p
!= ' ') p
++;
883 while (*p
&& *p
== ' ') p
++;
885 ret
= parse_injectargs(values
, tracker
, nargs
, oss
);
886 if (!nargs
.empty()) {
887 *oss
<< " failed to parse arguments: ";
889 for (std::vector
<const char*>::const_iterator i
= nargs
.begin();
890 i
!= nargs
.end(); ++i
) {
891 *oss
<< prefix
<< *i
;
897 update_legacy_vals(values
);
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
)
906 std::stringstream err
;
907 int ret
= set_val(values
, tracker
, key
, val
, &err
);
909 std::cerr
<< "set_val_or_die(" << key
<< "): " << err
.str();
911 ceph_assert(ret
== 0);
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
)
920 if (err_ss
) *err_ss
<< "No key specified";
929 string
k(ConfFile::normalize_key_name(key
));
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
);
937 if (err_ss
) *err_ss
<< "Set " << opt
.name
<< " to " << v
;
940 if (err_ss
) *err_ss
<< error_message
;
945 if (err_ss
) *err_ss
<< "Configuration option not found: '" << key
<< "'";
949 int md_config_t::rm_val(ConfigValues
& values
, const std::string_view key
)
951 return _rm_val(values
, key
, CONF_OVERRIDE
);
954 void md_config_t::get_defaults_bl(const ConfigValues
& values
,
957 if (defaults_bl
.length() == 0) {
960 for (const auto &i
: schema
) {
962 encode(i
.second
.name
, bl
);
963 auto [value
, found
] = values
.get_value(i
.second
.name
, CONF_DEFAULT
);
965 encode(Option::to_str(value
), bl
);
968 conf_stringify(_get_val_default(i
.second
), &val
);
972 encode(n
, defaults_bl
);
973 defaults_bl
.claim_append(bl
);
978 void md_config_t::get_config_bl(
979 const ConfigValues
& values
,
980 uint64_t have_version
,
982 uint64_t *got_version
)
984 if (values_bl
.length() == 0) {
987 values
.for_each([&](auto& name
, auto& configs
) {
988 if (name
== "fsid" ||
994 encode((uint32_t)configs
.size(), bl
);
995 for (auto& j
: configs
) {
997 encode(Option::to_str(j
.second
), bl
);
1000 // make sure overridden items appear, and include the default value
1001 for (auto& i
: ignored_mon_values
) {
1002 if (values
.contains(i
.first
)) {
1005 if (i
.first
== "fsid" ||
1006 i
.first
== "host") {
1009 const Option
*opt
= find_option(i
.first
);
1014 encode(i
.first
, bl
);
1015 encode((uint32_t)1, bl
);
1016 encode((int32_t)CONF_DEFAULT
, bl
);
1018 conf_stringify(_get_val_default(*opt
), &val
);
1021 encode(n
, values_bl
);
1022 values_bl
.claim_append(bl
);
1023 encode(ignored_mon_values
, values_bl
);
1024 ++values_bl_version
;
1026 if (have_version
!= values_bl_version
) {
1028 *got_version
= values_bl_version
;
1032 std::optional
<std::string
> md_config_t::get_val_default(std::string_view key
)
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
));
1039 return std::nullopt
;
1042 int md_config_t::get_val(const ConfigValues
& values
,
1043 const std::string_view key
, char **buf
, int len
) const
1045 string
k(ConfFile::normalize_key_name(key
));
1046 return _get_val_cstr(values
, k
, buf
, len
);
1049 int md_config_t::get_val(
1050 const ConfigValues
& values
,
1051 const std::string_view key
,
1052 std::string
*val
) const
1054 return conf_stringify(get_val_generic(values
, key
), val
);
1057 Option::value_t
md_config_t::get_val_generic(
1058 const ConfigValues
& values
,
1059 const std::string_view key
) const
1061 return _get_val(values
, key
);
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
1071 return Option::value_t(boost::blank());
1074 // In key names, leading and trailing whitespace are not significant.
1075 string
k(ConfFile::normalize_key_name(key
));
1077 const Option
*o
= find_option(k
);
1079 // not a valid config option
1080 return Option::value_t(boost::blank());
1083 return _get_val(values
, *o
, stack
, err
);
1086 Option::value_t
md_config_t::_get_val(
1087 const ConfigValues
& values
,
1089 expand_stack_t
*stack
,
1090 std::ostream
*err
) const
1092 expand_stack_t a_stack
;
1096 return _expand_meta(values
,
1097 _get_val_nometa(values
, o
),
1101 Option::value_t
md_config_t::_get_val_nometa(const ConfigValues
& values
,
1102 const Option
& o
) const
1104 if (auto [value
, found
] = values
.get_value(o
.name
, -1); found
) {
1107 return _get_val_default(o
);
1111 const Option::value_t
& md_config_t::_get_val_default(const Option
& o
) const
1113 bool has_daemon_default
= !boost::get
<boost::blank
>(&o
.daemon_value
);
1114 if (is_daemon
&& has_daemon_default
) {
1115 return o
.daemon_value
;
1121 void md_config_t::early_expand_meta(
1122 const ConfigValues
& values
,
1124 std::ostream
*err
) const
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
);
1133 bool md_config_t::finalize_reexpand_meta(ConfigValues
& values
,
1134 const ConfigTracker
& tracker
)
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
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
);
1148 return !may_reexpand_meta
.empty();
1151 Option::value_t
md_config_t::_expand_meta(
1152 const ConfigValues
& values
,
1153 const Option::value_t
& in
,
1155 expand_stack_t
*stack
,
1156 std::ostream
*err
) const
1158 //cout << __func__ << " in '" << in << "' stack " << stack << std::endl;
1162 const std::string
*str
= boost::get
<const std::string
>(&in
);
1168 auto pos
= str
->find('$');
1169 if (pos
== std::string::npos
) {
1170 // no substitutions!
1175 stack
->push_back(make_pair(o
, &in
));
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
);
1185 // try to parse the variable name into var, either \$\{(.+)\} or
1187 const char *valid_chars
= "abcdefghijklmnopqrstuvwxyz_";
1190 if ((*str
)[pos
+1] == '{') {
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);
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);
1204 var
= str
->substr(pos
+ 1);
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();
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");
1234 out
+= stringify(getpid());
1237 may_reexpand_meta
.push_back(o
->name
);
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();
1248 if (var
== "data_dir") {
1249 var
= data_dir_option
;
1251 const Option
*o
= find_option(var
);
1253 out
+= str
->substr(pos
, endpos
- pos
);
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
;
1260 if (match
!= stack
->end()) {
1261 // substitution loop; break the cycle
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";
1271 return Option::value_t(std::string("$") + o
->name
);
1273 // recursively evaluate!
1275 conf_stringify(_get_val(values
, *o
, stack
, err
), &n
);
1281 pos
= str
->find('$', last_pos
);
1283 if (last_pos
!= std::string::npos
) {
1284 out
+= str
->substr(last_pos
);
1290 return Option::value_t(out
);
1293 int md_config_t::_get_val_cstr(
1294 const ConfigValues
& values
,
1295 const std::string
& key
, char **buf
, int len
) const
1301 if (conf_stringify(_get_val(values
, key
), &val
) == 0) {
1302 int l
= val
.length() + 1;
1304 *buf
= (char*)malloc(l
);
1307 strncpy(*buf
, val
.c_str(), l
);
1310 snprintf(*buf
, len
, "%s", val
.c_str());
1311 return (l
> len
) ? -ENAMETOOLONG
: 0;
1314 // couldn't find a configuration option with key 'k'
1318 void md_config_t::get_all_keys(std::vector
<std::string
> *keys
) const {
1319 const std::string
negative_flag_prefix("no_");
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
);
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.
1337 std::vector
<std::string
>
1338 md_config_t::get_my_sections(const ConfigValues
& values
) const
1340 return {values
.name
.to_str(),
1341 values
.name
.get_type_name().data(),
1345 // Return a list of all sections
1346 int md_config_t::get_all_sections(std::vector
<std::string
> §ions
) const
1348 for (auto [section_name
, section
] : cf
) {
1349 sections
.push_back(section_name
);
1350 std::ignore
= section
;
1355 int md_config_t::get_val_from_conf_file(
1356 const ConfigValues
& values
,
1357 const std::vector
<std::string
> §ions
,
1358 const std::string_view key
,
1362 int r
= _get_val_from_conf_file(sections
, key
, out
);
1367 expand_stack_t stack
;
1368 auto v
= _expand_meta(values
, Option::value_t(out
), nullptr, &stack
, nullptr);
1369 conf_stringify(v
, &out
);
1374 int md_config_t::_get_val_from_conf_file(
1375 const std::vector
<std::string
> §ions
,
1376 const std::string_view key
,
1377 std::string
&out
) const
1379 for (auto &s
: sections
) {
1380 int ret
= cf
.read(s
, key
, out
);
1383 } else if (ret
!= -ENOENT
) {
1390 int md_config_t::_set_val(
1391 ConfigValues
& values
,
1392 const ConfigTracker
& observers
,
1393 const std::string
&raw_val
,
1396 std::string
*error_message
)
1398 Option::value_t new_value
;
1399 ceph_assert(error_message
);
1400 int r
= opt
.parse_value(raw_val
, &new_value
, error_message
);
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";
1417 // Apply the value to its entry in the `values` map
1418 auto result
= values
.set_value(opt
.name
, std::move(new_value
), level
);
1420 case ConfigValues::SET_NO_CHANGE
:
1422 case ConfigValues::SET_NO_EFFECT
:
1425 case ConfigValues::SET_HAVE_EFFECT
:
1427 _refresh(values
, opt
);
1433 void md_config_t::_refresh(ConfigValues
& values
, const Option
& opt
)
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
);
1440 // Was this a debug_* option update?
1441 if (opt
.subsys
>= 0) {
1443 conf_stringify(_get_val(values
, opt
), &actual_val
);
1444 values
.set_logging(opt
.subsys
, actual_val
.c_str());
1446 // normal option, advertise the change.
1447 values
.changed
.insert(opt
.name
);
1451 int md_config_t::_rm_val(ConfigValues
& values
,
1452 const std::string_view key
,
1455 if (schema
.count(key
) == 0) {
1458 auto ret
= values
.rm_val(std::string
{key
}, level
);
1462 if (ret
== ConfigValues::SET_HAVE_EFFECT
) {
1463 _refresh(values
, *find_option(key
));
1470 template<typename Size
>
1471 struct get_size_visitor
: public boost::static_visitor
<Size
>
1473 get_size_visitor() {}
1475 template<typename T
>
1476 Size
operator()(const T
&) const {
1479 Size
operator()(const Option::size_t& sz
) const {
1480 return static_cast<Size
>(sz
.value
);
1482 Size
operator()(const Size
& v
) const {
1488 * Handles assigning from a variant-of-types to a variant-of-pointers-to-types
1490 template<class Config
>
1491 class assign_visitor
: public boost::static_visitor
<>
1494 Option::value_t val
;
1497 assign_visitor(Config
*conf_
, Option::value_t val_
)
1498 : conf(conf_
), val(val_
)
1501 template <typename T
>
1502 void operator()(T
Config::* ptr
) const
1504 T
*member
= const_cast<T
*>(&(conf
->*(boost::get
<const T
Config::*>(ptr
))));
1506 *member
= boost::get
<T
>(val
);
1508 void operator()(uint64_t Config::* ptr
) const
1511 auto member
= const_cast<T
*>(&(conf
->*(boost::get
<const T
Config::*>(ptr
))));
1512 *member
= boost::apply_visitor(get_size_visitor
<T
>{}, val
);
1514 void operator()(int64_t Config::* ptr
) const
1517 auto member
= const_cast<T
*>(&(conf
->*(boost::get
<const T
Config::*>(ptr
))));
1518 *member
= boost::apply_visitor(get_size_visitor
<T
>{}, val
);
1521 } // anonymous namespace
1523 void md_config_t::update_legacy_vals(ConfigValues
& values
)
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
);
1533 void md_config_t::update_legacy_val(ConfigValues
& values
,
1535 md_config_t::member_ptr_t member_ptr
)
1537 Option::value_t v
= _get_val(values
, opt
);
1538 boost::apply_visitor(assign_visitor(&values
, v
), member_ptr
);
1541 static void dump(Formatter
*f
, int level
, Option::value_t in
)
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
);
1552 f
->dump_stream(ceph_conf_level_name(level
)) << Option::to_str(in
);
1556 void md_config_t::diff(
1557 const ConfigValues
& values
,
1561 values
.for_each([this, f
, &values
] (auto& name
, auto& configs
) {
1562 if (configs
.empty()) {
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
));
1572 for (auto& j
: configs
) {
1573 dump(f
, j
.first
, j
.second
);
1575 dump(f
, CONF_FINAL
, _get_val(values
, *o
));
1580 void md_config_t::complain_about_parse_error(CephContext
*cct
)
1582 ::complain_about_parse_error(cct
, parse_error
);