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 "common/ceph_argparse.h"
16 #include "common/common_init.h"
17 #include "common/config.h"
18 #include "include/str_list.h"
19 #include "include/stringify.h"
20 #include "osd/osd_types.h"
21 #include "common/errno.h"
22 #include "common/hostname.h"
24 #include <boost/type_traits.hpp>
26 /* Don't use standard Ceph logging in this file.
27 * We can't use logging until it's initialized, and a lot of the necessary
28 * initialization happens here.
40 using std::ostringstream
;
44 const char *CEPH_CONF_FILE_DEFAULT
= "$data_dir/config, /etc/ceph/$cluster.conf, ~/.ceph/$cluster.conf, $cluster.conf"
45 #if defined(__FreeBSD__)
46 ", /usr/local/etc/ceph/$cluster.conf"
51 #define STRINGIFY(x) _STR(x)
53 int ceph_resolve_file_search(const std::string
& filename_list
,
57 get_str_list(filename_list
, ls
);
60 list
<string
>::iterator iter
;
61 for (iter
= ls
.begin(); iter
!= ls
.end(); ++iter
) {
62 int fd
= ::open(iter
->c_str(), O_RDONLY
|O_CLOEXEC
);
77 md_config_t::md_config_t(bool is_daemon
)
79 lock("md_config_t", true, false)
83 // Load the compile-time list of Option into
84 // a map so that we can resolve keys quickly.
85 for (const auto &i
: ceph_options
) {
86 if (schema
.count(i
.name
)) {
87 // We may be instantiated pre-logging so send
88 std::cerr
<< "Duplicate config key in schema: '" << i
.name
<< "'"
92 schema
.insert({i
.name
, i
});
95 // Populate list of legacy_values according to the OPTION() definitions
96 // Note that this is just setting up our map of name->member ptr. The
97 // default values etc will get loaded in along with new-style data,
98 // as all loads write to both the values map, and the legacy
99 // members if present.
101 #define OPTION(name, type) \
102 {std::string(STRINGIFY(name)), &md_config_t::name},
103 #define SAFE_OPTION(name, type) OPTION(name, type)
104 #include "common/legacy_config_opts.h"
111 // Load default values from the schema
112 for (const auto &i
: schema
) {
113 const Option
&opt
= i
.second
;
114 bool has_daemon_default
= !boost::get
<boost::blank
>(&opt
.daemon_value
);
115 Option::value_t default_val
;
116 if (is_daemon
&& has_daemon_default
) {
117 default_val
= opt
.daemon_value
;
119 default_val
= opt
.value
;
122 if (opt
.type
== Option::TYPE_STR
) {
123 // We call pre_validate as a sanity check, but also to get any
124 // side effect (value modification) from the validator.
125 std::string
*def_str
= boost::get
<std::string
>(&default_val
);
127 if (opt
.pre_validate(def_str
, &err
) != 0) {
128 std::cerr
<< "Default value " << opt
.name
<< "=" << *def_str
<< " is "
129 "invalid: " << err
<< std::endl
;
131 // This is the compiled-in default that is failing its own option's
132 // validation, so this is super-invalid and should never make it
133 // past a pull request: crash out.
138 values
[i
.first
] = default_val
;
141 // Copy out values (defaults) into any legacy (C struct member) fields
142 for (const auto &i
: legacy_values
) {
143 const auto &name
= i
.first
;
144 const auto &option
= schema
.at(name
);
147 update_legacy_val(option
, ptr
);
152 * Sanity check schema. Assert out on failures, to ensure any bad changes
153 * cannot possibly pass any testing and make it into a release.
155 void md_config_t::validate_schema()
157 for (const auto &i
: schema
) {
158 const auto &opt
= i
.second
;
159 for (const auto &see_also_key
: opt
.see_also
) {
160 if (schema
.count(see_also_key
) == 0) {
161 std::cerr
<< "Non-existent see-also key '" << see_also_key
162 << "' on option '" << opt
.name
<< "'" << std::endl
;
168 for (const auto &i
: legacy_values
) {
169 if (schema
.count(i
.first
) == 0) {
170 std::cerr
<< "Schema is missing legacy field '" << i
.first
<< "'"
177 void md_config_t::init_subsys()
179 #define SUBSYS(name, log, gather) \
180 subsys.add(ceph_subsys_##name, STRINGIFY(name), log, gather);
181 #define DEFAULT_SUBSYS(log, gather) \
182 subsys.add(ceph_subsys_, "none", log, gather);
183 #include "common/subsys.h"
185 #undef DEFAULT_SUBSYS
188 md_config_t::~md_config_t()
192 void md_config_t::add_observer(md_config_obs_t
* observer_
)
194 Mutex::Locker
l(lock
);
195 const char **keys
= observer_
->get_tracked_conf_keys();
196 for (const char ** k
= keys
; *k
; ++k
) {
197 obs_map_t::value_type
val(*k
, observer_
);
198 observers
.insert(val
);
202 void md_config_t::remove_observer(md_config_obs_t
* observer_
)
204 Mutex::Locker
l(lock
);
205 bool found_obs
= false;
206 for (obs_map_t::iterator o
= observers
.begin(); o
!= observers
.end(); ) {
207 if (o
->second
== observer_
) {
208 observers
.erase(o
++);
218 int md_config_t::parse_config_files(const char *conf_files
,
219 std::ostream
*warnings
,
222 Mutex::Locker
l(lock
);
224 if (internal_safe_to_start_threads
)
227 if (!cluster
.size() && !conf_files
) {
229 * set the cluster name to 'ceph' when neither cluster name nor
230 * configuration file are specified.
236 const char *c
= getenv("CEPH_CONF");
241 if (flags
& CINIT_FLAG_NO_DEFAULT_CONFIG_FILE
)
243 conf_files
= CEPH_CONF_FILE_DEFAULT
;
247 std::list
<std::string
> cfl
;
248 get_str_list(conf_files
, cfl
);
250 auto p
= cfl
.begin();
251 while (p
!= cfl
.end()) {
254 if (s
.find("$data_dir") != string::npos
) {
255 if (data_dir_option
.length()) {
256 list
<const Option
*> stack
;
257 expand_meta(s
, NULL
, stack
, warnings
);
260 cfl
.erase(p
++); // ignore this item
266 return parse_config_files_impl(cfl
, warnings
);
269 int md_config_t::parse_config_files_impl(const std::list
<std::string
> &conf_files
,
270 std::ostream
*warnings
)
272 assert(lock
.is_locked());
275 list
<string
>::const_iterator c
;
276 for (c
= conf_files
.begin(); c
!= conf_files
.end(); ++c
) {
279 expand_meta(fn
, warnings
);
280 int ret
= cf
.parse_file(fn
.c_str(), &parse_errors
, warnings
);
283 else if (ret
!= -ENOENT
)
286 // it must have been all ENOENTs, that's the only way we got here
287 if (c
== conf_files
.end())
290 if (cluster
.size() == 0) {
292 * If cluster name is not set yet, use the prefix of the
293 * basename of configuration file as cluster name.
295 auto start
= c
->rfind('/') + 1;
296 auto end
= c
->find(".conf", start
);
297 if (end
== c
->npos
) {
299 * If the configuration file does not follow $cluster.conf
300 * convention, we do the last try and assign the cluster to
305 cluster
= c
->substr(start
, end
- start
);
309 std::vector
<std::string
> my_sections
;
310 _get_my_sections(my_sections
);
311 for (const auto &i
: schema
) {
312 const auto &opt
= i
.second
;
314 int ret
= _get_val_from_conf_file(my_sections
, opt
.name
, val
, false);
316 std::string error_message
;
317 int r
= set_val_impl(val
, opt
, &error_message
);
318 if (warnings
!= nullptr && (r
!= 0 || !error_message
.empty())) {
319 *warnings
<< "parse error setting '" << opt
.name
<< "' to '" << val
321 if (!error_message
.empty()) {
322 *warnings
<< " (" << error_message
<< ")";
324 *warnings
<< std::endl
;
330 for (size_t o
= 0; o
< subsys
.get_num(); o
++) {
331 std::string
as_option("debug_");
332 as_option
+= subsys
.get_name(o
);
334 int ret
= _get_val_from_conf_file(my_sections
, as_option
.c_str(), val
, false);
337 int r
= sscanf(val
.c_str(), "%d/%d", &log
, &gather
);
341 // cout << "config subsys " << subsys.get_name(o) << " log " << log << " gather " << gather << std::endl;
342 subsys
.set_log_level(o
, log
);
343 subsys
.set_gather_level(o
, gather
);
348 // Warn about section names that look like old-style section names
349 std::deque
< std::string
> old_style_section_names
;
350 for (ConfFile::const_section_iter_t s
= cf
.sections_begin();
351 s
!= cf
.sections_end(); ++s
) {
352 const string
&str(s
->first
);
353 if (((str
.find("mds") == 0) || (str
.find("mon") == 0) ||
354 (str
.find("osd") == 0)) && (str
.size() > 3) && (str
[3] != '.')) {
355 old_style_section_names
.push_back(str
);
358 if (!old_style_section_names
.empty()) {
360 cerr
<< "ERROR! old-style section name(s) found: ";
362 for (std::deque
< std::string
>::const_iterator os
= old_style_section_names
.begin();
363 os
!= old_style_section_names
.end(); ++os
) {
367 cerr
<< ". Please use the new style section names that include a period.";
372 void md_config_t::parse_env()
374 Mutex::Locker
l(lock
);
375 if (internal_safe_to_start_threads
)
377 if (getenv("CEPH_KEYRING")) {
378 set_val_or_die("keyring", getenv("CEPH_KEYRING"));
382 void md_config_t::show_config(std::ostream
& out
)
384 Mutex::Locker
l(lock
);
385 _show_config(&out
, NULL
);
388 void md_config_t::show_config(Formatter
*f
)
390 Mutex::Locker
l(lock
);
391 _show_config(NULL
, f
);
394 void md_config_t::config_options(Formatter
*f
)
396 Mutex::Locker
l(lock
);
397 f
->open_array_section("options");
398 for (const auto& i
: schema
) {
399 const Option
&opt
= i
.second
;
405 void md_config_t::_show_config(std::ostream
*out
, Formatter
*f
)
408 *out
<< "name = " << name
<< std::endl
;
409 *out
<< "cluster = " << cluster
<< std::endl
;
412 f
->dump_string("name", stringify(name
));
413 f
->dump_string("cluster", cluster
);
415 for (size_t o
= 0; o
< subsys
.get_num(); o
++) {
417 *out
<< "debug_" << subsys
.get_name(o
)
418 << " = " << subsys
.get_log_level(o
)
419 << "/" << subsys
.get_gather_level(o
) << std::endl
;
422 std::string debug_name
= "debug_";
423 debug_name
+= subsys
.get_name(o
);
424 ss
<< subsys
.get_log_level(o
)
425 << "/" << subsys
.get_gather_level(o
);
426 f
->dump_string(debug_name
.c_str(), ss
.str());
429 for (const auto& i
: schema
) {
430 const Option
&opt
= i
.second
;
432 _get_val(opt
.name
, &buf
, -1);
434 *out
<< opt
.name
<< " = " << buf
<< std::endl
;
436 f
->dump_string(opt
.name
.c_str(), buf
);
441 int md_config_t::parse_argv(std::vector
<const char*>& args
)
443 Mutex::Locker
l(lock
);
444 if (internal_safe_to_start_threads
) {
448 bool show_config
= false;
449 bool show_config_value
= false;
450 string show_config_value_arg
;
452 // In this function, don't change any parts of the configuration directly.
453 // Instead, use set_val to set them. This will allow us to send the proper
454 // observer notifications later.
456 for (std::vector
<const char*>::iterator i
= args
.begin(); i
!= args
.end(); ) {
457 if (strcmp(*i
, "--") == 0) {
458 /* Normally we would use ceph_argparse_double_dash. However, in this
459 * function we *don't* want to remove the double dash, because later
460 * argument parses will still need to see it. */
463 else if (ceph_argparse_flag(args
, i
, "--show_conf", (char*)NULL
)) {
464 cerr
<< cf
<< std::endl
;
467 else if (ceph_argparse_flag(args
, i
, "--show_config", (char*)NULL
)) {
470 else if (ceph_argparse_witharg(args
, i
, &val
, "--show_config_value", (char*)NULL
)) {
471 show_config_value
= true;
472 show_config_value_arg
= val
;
474 else if (ceph_argparse_flag(args
, i
, "--foreground", "-f", (char*)NULL
)) {
475 set_val_or_die("daemonize", "false");
477 else if (ceph_argparse_flag(args
, i
, "-d", (char*)NULL
)) {
478 set_val_or_die("daemonize", "false");
479 set_val_or_die("log_file", "");
480 set_val_or_die("log_to_stderr", "true");
481 set_val_or_die("err_to_stderr", "true");
482 set_val_or_die("log_to_syslog", "false");
484 // Some stuff that we wanted to give universal single-character options for
485 // Careful: you can burn through the alphabet pretty quickly by adding
487 else if (ceph_argparse_witharg(args
, i
, &val
, "--monmap", "-M", (char*)NULL
)) {
488 set_val_or_die("monmap", val
.c_str());
490 else if (ceph_argparse_witharg(args
, i
, &val
, "--mon_host", "-m", (char*)NULL
)) {
491 set_val_or_die("mon_host", val
.c_str());
493 else if (ceph_argparse_witharg(args
, i
, &val
, "--bind", (char*)NULL
)) {
494 set_val_or_die("public_addr", val
.c_str());
496 else if (ceph_argparse_witharg(args
, i
, &val
, "--keyfile", "-K", (char*)NULL
)) {
497 set_val_or_die("keyfile", val
.c_str());
499 else if (ceph_argparse_witharg(args
, i
, &val
, "--keyring", "-k", (char*)NULL
)) {
500 set_val_or_die("keyring", val
.c_str());
502 else if (ceph_argparse_witharg(args
, i
, &val
, "--client_mountpoint", "-r", (char*)NULL
)) {
503 set_val_or_die("client_mountpoint", val
.c_str());
506 int r
= parse_option(args
, i
, NULL
);
515 _show_config(&cout
, NULL
);
519 if (show_config_value
) {
521 int r
= _get_val(show_config_value_arg
.c_str(), &buf
, -1);
524 std::cerr
<< "failed to get config option '" <<
525 show_config_value_arg
<< "': option not found" << std::endl
;
527 std::cerr
<< "failed to get config option '" <<
528 show_config_value_arg
<< "': " << cpp_strerror(r
) << std::endl
;
532 expand_meta(s
, &std::cerr
);
533 std::cout
<< s
<< std::endl
;
540 int md_config_t::parse_option(std::vector
<const char*>& args
,
541 std::vector
<const char*>::iterator
& i
,
549 for (o
= 0; o
< subsys
.get_num(); o
++) {
550 std::string
as_option("--");
551 as_option
+= "debug_";
552 as_option
+= subsys
.get_name(o
);
554 if (ceph_argparse_witharg(args
, i
, &val
, err
,
555 as_option
.c_str(), (char*)NULL
)) {
564 int r
= sscanf(val
.c_str(), "%d/%d", &log
, &gather
);
568 // cout << "subsys " << subsys.get_name(o) << " log " << log << " gather " << gather << std::endl;
569 subsys
.set_log_level(o
, log
);
570 subsys
.set_gather_level(o
, gather
);
572 *oss
<< "debug_" << subsys
.get_name(o
) << "=" << log
<< "/" << gather
<< " ";
577 if (o
< subsys
.get_num()) {
581 std::string option_name
;
582 std::string error_message
;
584 for (const auto& opt_iter
: schema
) {
585 const Option
&opt
= opt_iter
.second
;
587 std::string
as_option("--");
588 as_option
+= opt
.name
;
589 option_name
= opt
.name
;
590 if (opt
.type
== Option::TYPE_BOOL
) {
592 if (ceph_argparse_binary_flag(args
, i
, &res
, oss
, as_option
.c_str(),
595 ret
= set_val_impl("false", opt
, &error_message
);
597 ret
= set_val_impl("true", opt
, &error_message
);
602 std::string
no("--no-");
604 if (ceph_argparse_flag(args
, i
, no
.c_str(), (char*)NULL
)) {
605 ret
= set_val_impl("false", opt
, &error_message
);
609 } else if (ceph_argparse_witharg(args
, i
, &val
, err
,
610 as_option
.c_str(), (char*)NULL
)) {
611 if (!err
.str().empty()) {
612 error_message
= err
.str();
616 if (oss
&& ((!opt
.is_safe()) &&
617 (observers
.find(opt
.name
) == observers
.end()))) {
618 *oss
<< "You cannot change " << opt
.name
<< " using injectargs.\n";
621 ret
= set_val_impl(val
, opt
, &error_message
);
627 if (ret
!= 0 || !error_message
.empty()) {
628 assert(!option_name
.empty());
630 *oss
<< "Parse error setting " << option_name
<< " to '"
631 << val
<< "' using injectargs";
632 if (!error_message
.empty()) {
633 *oss
<< " (" << error_message
<< ")";
637 cerr
<< "parse error setting '" << option_name
<< "' to '"
639 if (!error_message
.empty()) {
640 cerr
<< " (" << error_message
<< ")";
642 cerr
<< "\n" << std::endl
;
646 if (o
== schema
.size()) {
653 int md_config_t::parse_injectargs(std::vector
<const char*>& args
,
656 assert(lock
.is_locked());
658 for (std::vector
<const char*>::iterator i
= args
.begin(); i
!= args
.end(); ) {
659 int r
= parse_option(args
, i
, oss
);
666 void md_config_t::apply_changes(std::ostream
*oss
)
668 Mutex::Locker
l(lock
);
670 * apply changes until the cluster name is assigned
676 bool md_config_t::_internal_field(const string
& s
)
678 if (s
== "internal_safe_to_start_threads")
683 void md_config_t::_apply_changes(std::ostream
*oss
)
685 /* Maps observers to the configuration options that they care about which
687 typedef std::map
< md_config_obs_t
*, std::set
<std::string
> > rev_obs_map_t
;
691 // expand_all_meta could have modified anything. Copy it all out again.
692 for (const auto &i
: legacy_values
) {
693 const auto &name
= i
.first
;
694 const auto &option
= schema
.at(name
);
697 update_legacy_val(option
, ptr
);
700 // create the reverse observer mapping, mapping observers to the set of
701 // changed keys that they'll get.
703 std::set
<std::string
> empty_set
;
705 char *bufptr
= (char*)buf
;
706 for (changed_set_t::const_iterator c
= changed
.begin();
707 c
!= changed
.end(); ++c
) {
708 const std::string
&key(*c
);
709 pair
< obs_map_t::iterator
, obs_map_t::iterator
>
710 range(observers
.equal_range(key
));
712 (!_get_val(key
.c_str(), &bufptr
, sizeof(buf
))) &&
713 !_internal_field(key
)) {
714 (*oss
) << key
<< " = '" << buf
<< "' ";
715 if (range
.first
== range
.second
) {
716 (*oss
) << "(not observed, change may require restart) ";
719 for (obs_map_t::iterator r
= range
.first
; r
!= range
.second
; ++r
) {
720 rev_obs_map_t::value_type
robs_val(r
->second
, empty_set
);
721 pair
< rev_obs_map_t::iterator
, bool > robs_ret(robs
.insert(robs_val
));
722 std::set
<std::string
> &keys(robs_ret
.first
->second
);
729 // Make any pending observer callbacks
730 for (rev_obs_map_t::const_iterator r
= robs
.begin(); r
!= robs
.end(); ++r
) {
731 md_config_obs_t
*obs
= r
->first
;
732 obs
->handle_conf_change(this, r
->second
);
737 void md_config_t::call_all_observers()
739 std::map
<md_config_obs_t
*,std::set
<std::string
> > obs
;
741 Mutex::Locker
l(lock
);
745 for (auto r
= observers
.begin(); r
!= observers
.end(); ++r
) {
746 obs
[r
->second
].insert(r
->first
);
749 for (auto p
= obs
.begin();
752 p
->first
->handle_conf_change(this, p
->second
);
756 int md_config_t::injectargs(const std::string
& s
, std::ostream
*oss
)
759 Mutex::Locker
l(lock
);
760 char b
[s
.length()+1];
761 strcpy(b
, s
.c_str());
762 std::vector
<const char*> nargs
;
766 while (*p
&& *p
!= ' ') p
++;
770 while (*p
&& *p
== ' ') p
++;
772 ret
= parse_injectargs(nargs
, oss
);
773 if (!nargs
.empty()) {
774 *oss
<< " failed to parse arguments: ";
776 for (std::vector
<const char*>::const_iterator i
= nargs
.begin();
777 i
!= nargs
.end(); ++i
) {
778 *oss
<< prefix
<< *i
;
788 void md_config_t::set_val_or_die(const std::string
&key
,
789 const std::string
&val
,
792 std::stringstream err
;
793 int ret
= set_val(key
, val
, meta
, &err
);
795 std::cerr
<< "set_val_or_die(" << key
<< "): " << err
.str();
800 int md_config_t::set_val(const std::string
&key
, const char *val
,
801 bool meta
, std::stringstream
*err_ss
)
803 Mutex::Locker
l(lock
);
805 if (err_ss
) *err_ss
<< "No key specified";
814 expand_meta(v
, &std::cerr
);
816 string
k(ConfFile::normalize_key_name(key
));
819 if (strncmp(k
.c_str(), "debug_", 6) == 0) {
820 for (size_t o
= 0; o
< subsys
.get_num(); o
++) {
821 std::string as_option
= "debug_" + subsys
.get_name(o
);
822 if (k
== as_option
) {
824 int r
= sscanf(v
.c_str(), "%d/%d", &log
, &gather
);
829 subsys
.set_log_level(o
, log
);
830 subsys
.set_gather_level(o
, gather
);
831 if (err_ss
) *err_ss
<< "Set " << k
<< " to " << log
<< "/" << gather
;
835 *err_ss
<< "Invalid debug level, should be <int> or <int>/<int>";
842 const auto &opt_iter
= schema
.find(k
);
843 if (opt_iter
!= schema
.end()) {
844 const Option
&opt
= opt_iter
->second
;
845 if ((!opt
.is_safe()) && internal_safe_to_start_threads
) {
846 // If threads have been started and the option is not thread safe
847 if (observers
.find(opt
.name
) == observers
.end()) {
848 // And there is no observer to safely change it...
850 if (err_ss
) *err_ss
<< "Configuration option '" << key
<< "' may "
851 "not be modified at runtime";
856 std::string error_message
;
857 int r
= set_val_impl(v
, opt
, &error_message
);
859 if (err_ss
) *err_ss
<< "Set " << opt
.name
<< " to " << v
;
861 if (err_ss
) *err_ss
<< error_message
;
866 if (err_ss
) *err_ss
<< "Configuration option not found: '" << key
<< "'";
871 int md_config_t::get_val(const std::string
&key
, char **buf
, int len
) const
873 Mutex::Locker
l(lock
);
874 return _get_val(key
, buf
,len
);
877 Option::value_t
md_config_t::get_val_generic(const std::string
&key
) const
879 Mutex::Locker
l(lock
);
880 return _get_val(key
);
883 Option::value_t
md_config_t::_get_val(const std::string
&key
) const
885 assert(lock
.is_locked());
888 return Option::value_t(boost::blank());
891 // In key names, leading and trailing whitespace are not significant.
892 string
k(ConfFile::normalize_key_name(key
));
894 const auto &opt_iter
= schema
.find(k
);
895 if (opt_iter
!= schema
.end()) {
896 // Using .at() is safe because all keys in the schema always have
897 // entries in ::values
900 return Option::value_t(boost::blank());
904 int md_config_t::_get_val(const std::string
&key
, std::string
*value
) const {
905 assert(lock
.is_locked());
907 std::string
normalized_key(ConfFile::normalize_key_name(key
));
908 Option::value_t config_value
= _get_val(normalized_key
.c_str());
909 if (!boost::get
<boost::blank
>(&config_value
)) {
911 if (bool *flag
= boost::get
<bool>(&config_value
)) {
912 oss
<< (*flag
? "true" : "false");
913 } else if (double *dp
= boost::get
<double>(&config_value
)) {
914 oss
<< std::fixed
<< *dp
;
924 int md_config_t::_get_val(const std::string
&key
, char **buf
, int len
) const
926 assert(lock
.is_locked());
932 if (_get_val(key
, &val
) == 0) {
933 int l
= val
.length() + 1;
935 *buf
= (char*)malloc(l
);
938 strncpy(*buf
, val
.c_str(), l
);
941 snprintf(*buf
, len
, "%s", val
.c_str());
942 return (l
> len
) ? -ENAMETOOLONG
: 0;
945 string
k(ConfFile::normalize_key_name(key
));
947 for (size_t o
= 0; o
< subsys
.get_num(); o
++) {
948 std::string as_option
= "debug_" + subsys
.get_name(o
);
949 if (k
== as_option
) {
951 *buf
= (char*)malloc(20);
954 int l
= snprintf(*buf
, len
, "%d/%d", subsys
.get_log_level(o
), subsys
.get_gather_level(o
));
955 return (l
== len
) ? -ENAMETOOLONG
: 0;
959 // couldn't find a configuration option with key 'k'
963 void md_config_t::get_all_keys(std::vector
<std::string
> *keys
) const {
964 const std::string
negative_flag_prefix("no_");
967 keys
->reserve(schema
.size());
968 for (const auto &i
: schema
) {
969 const Option
&opt
= i
.second
;
970 keys
->push_back(opt
.name
);
971 if (opt
.type
== Option::TYPE_BOOL
) {
972 keys
->push_back(negative_flag_prefix
+ opt
.name
);
975 for (size_t i
= 0; i
< subsys
.get_num(); ++i
) {
976 keys
->push_back("debug_" + subsys
.get_name(i
));
980 /* The order of the sections here is important. The first section in the
981 * vector is the "highest priority" section; if we find it there, we'll stop
982 * looking. The lowest priority section is the one we look in only if all
983 * others had nothing. This should always be the global section.
985 void md_config_t::get_my_sections(std::vector
<std::string
> §ions
) const
987 Mutex::Locker
l(lock
);
988 _get_my_sections(sections
);
991 void md_config_t::_get_my_sections(std::vector
<std::string
> §ions
) const
993 assert(lock
.is_locked());
994 sections
.push_back(name
.to_str());
996 sections
.push_back(name
.get_type_name());
998 sections
.push_back("global");
1001 // Return a list of all sections
1002 int md_config_t::get_all_sections(std::vector
<std::string
> §ions
) const
1004 Mutex::Locker
l(lock
);
1005 for (ConfFile::const_section_iter_t s
= cf
.sections_begin();
1006 s
!= cf
.sections_end(); ++s
) {
1007 sections
.push_back(s
->first
);
1012 int md_config_t::get_val_from_conf_file(const std::vector
<std::string
> §ions
,
1013 const std::string
&key
, std::string
&out
, bool emeta
) const
1015 Mutex::Locker
l(lock
);
1016 return _get_val_from_conf_file(sections
, key
, out
, emeta
);
1019 int md_config_t::_get_val_from_conf_file(const std::vector
<std::string
> §ions
,
1020 const std::string
&key
, std::string
&out
, bool emeta
) const
1022 assert(lock
.is_locked());
1023 std::vector
<std::string
>::const_iterator s
= sections
.begin();
1024 std::vector
<std::string
>::const_iterator s_end
= sections
.end();
1025 for (; s
!= s_end
; ++s
) {
1026 int ret
= cf
.read(s
->c_str(), key
, out
);
1029 expand_meta(out
, &std::cerr
);
1032 else if (ret
!= -ENOENT
)
1038 int md_config_t::set_val_impl(const std::string
&raw_val
, const Option
&opt
,
1039 std::string
*error_message
)
1041 assert(lock
.is_locked());
1043 std::string val
= raw_val
;
1045 int r
= opt
.pre_validate(&val
, error_message
);
1050 Option::value_t new_value
;
1051 if (opt
.type
== Option::TYPE_INT
) {
1052 int64_t f
= strict_si_cast
<int64_t>(val
.c_str(), error_message
);
1053 if (!error_message
->empty()) {
1057 } else if (opt
.type
== Option::TYPE_UINT
) {
1058 uint64_t f
= strict_si_cast
<uint64_t>(val
.c_str(), error_message
);
1059 if (!error_message
->empty()) {
1063 } else if (opt
.type
== Option::TYPE_STR
) {
1065 } else if (opt
.type
== Option::TYPE_FLOAT
) {
1066 double f
= strict_strtod(val
.c_str(), error_message
);
1067 if (!error_message
->empty()) {
1072 } else if (opt
.type
== Option::TYPE_BOOL
) {
1073 if (strcasecmp(val
.c_str(), "false") == 0) {
1075 } else if (strcasecmp(val
.c_str(), "true") == 0) {
1078 int b
= strict_strtol(val
.c_str(), 10, error_message
);
1079 if (!error_message
->empty()) {
1084 } else if (opt
.type
== Option::TYPE_ADDR
) {
1086 if (!addr
.parse(val
.c_str())){
1090 } else if (opt
.type
== Option::TYPE_UUID
) {
1092 if (!uuid
.parse(val
.c_str())) {
1100 r
= opt
.validate(new_value
, error_message
);
1106 // Apply the value to its entry in the `values` map
1107 values
[opt
.name
] = new_value
;
1109 // Apply the value to its legacy field, if it has one
1110 auto legacy_ptr_iter
= legacy_values
.find(std::string(opt
.name
));
1111 if (legacy_ptr_iter
!= legacy_values
.end()) {
1112 update_legacy_val(opt
, legacy_ptr_iter
->second
);
1115 changed
.insert(opt
.name
);
1120 * Handles assigning from a variant-of-types to a variant-of-pointers-to-types
1122 class assign_visitor
: public boost::static_visitor
<>
1125 Option::value_t val
;
1128 assign_visitor(md_config_t
*conf_
, Option::value_t val_
)
1129 : conf(conf_
), val(val_
)
1132 template <typename T
>
1133 void operator()( T
md_config_t::* ptr
) const
1135 T
*member
= const_cast<T
*>(&(conf
->*(boost::get
<const T
md_config_t::*>(ptr
))));
1137 *member
= boost::get
<T
>(val
);
1141 void md_config_t::update_legacy_val(const Option
&opt
,
1142 md_config_t::member_ptr_t member_ptr
)
1144 if (boost::get
<boost::blank
>(&values
.at(opt
.name
))) {
1145 // This shouldn't happen, but if it does then just don't even
1146 // try to assign to the legacy field.
1150 boost::apply_visitor(assign_visitor(this, values
.at(opt
.name
)), member_ptr
);
1154 static const char *CONF_METAVARIABLES
[] = {
1155 "data_dir", // put this first: it may contain some of the others
1156 "cluster", "type", "name", "host", "num", "id", "pid", "cctid"
1158 static const int NUM_CONF_METAVARIABLES
=
1159 (sizeof(CONF_METAVARIABLES
) / sizeof(CONF_METAVARIABLES
[0]));
1161 void md_config_t::expand_all_meta()
1163 // Expand all metavariables
1165 for (const auto &i
: schema
) {
1166 const Option
&opt
= i
.second
;
1168 if (opt
.type
== Option::TYPE_STR
) {
1169 list
<const Option
*> stack
;
1170 std::string
*str
= boost::get
<std::string
>(&(values
.at(opt
.name
)));
1171 assert(str
!= nullptr); // Non-string values should never get in
1172 expand_meta(*str
, &opt
, stack
, &oss
);
1178 bool md_config_t::expand_meta(std::string
&val
,
1179 std::ostream
*oss
) const
1181 list
<const Option
*> stack
;
1182 return expand_meta(val
, NULL
, stack
, oss
);
1185 bool md_config_t::expand_meta(std::string
&origval
,
1187 std::list
<const Option
*> stack
,
1188 std::ostream
*oss
) const
1190 assert(lock
.is_locked());
1192 // no $ means no variable expansion is necessary
1193 if (origval
.find("$") == string::npos
)
1196 // ignore an expansion loop and create a human readable
1199 for (const auto stack_ptr
: stack
) {
1200 if (opt
->name
== stack_ptr
->name
) {
1201 *oss
<< "variable expansion loop at "
1202 << opt
->name
<< "=" << origval
<< std::endl
;
1203 *oss
<< "expansion stack: " << std::endl
;
1204 for (const auto j
: stack
) {
1206 _get_val(j
->name
, &val
);
1207 *oss
<< j
->name
<< "=" << val
<< std::endl
;
1213 stack
.push_front(opt
);
1216 bool found_meta
= false;
1218 string val
= origval
;
1219 for (string::size_type s
= 0; s
< val
.size(); ) {
1220 if (val
[s
] != '$') {
1225 // try to parse the variable name into var, either \$\{(.+)\} or
1227 const char *valid_chars
= "abcdefghijklmnopqrstuvwxyz_";
1230 if (val
[s
+1] == '{') {
1232 endpos
= val
.find_first_not_of(valid_chars
, s
+2);
1233 if (endpos
!= std::string::npos
&&
1234 val
[endpos
] == '}') {
1235 var
= val
.substr(s
+2, endpos
-s
-2);
1240 endpos
= val
.find_first_not_of(valid_chars
, s
+1);
1241 if (endpos
!= std::string::npos
)
1242 var
= val
.substr(s
+1, endpos
-s
-1);
1244 var
= val
.substr(s
+1);
1247 bool expanded
= false;
1249 // special metavariable?
1250 for (int i
= 0; i
< NUM_CONF_METAVARIABLES
; ++i
) {
1251 if (var
!= CONF_METAVARIABLES
[i
])
1253 //cout << " meta match of " << var << " " << CONF_METAVARIABLES[i] << std::endl;
1255 out
+= name
.get_type_name();
1256 else if (var
== "cluster")
1258 else if (var
== "name")
1259 out
+= name
.to_cstr();
1260 else if (var
== "host")
1263 out
+= ceph_get_short_hostname();
1267 else if (var
== "num")
1268 out
+= name
.get_id().c_str();
1269 else if (var
== "id")
1270 out
+= name
.get_id().c_str();
1271 else if (var
== "pid")
1272 out
+= stringify(getpid());
1273 else if (var
== "cctid")
1274 out
+= stringify((unsigned long long)this);
1275 else if (var
== "data_dir") {
1276 if (data_dir_option
.length()) {
1278 _get_val(data_dir_option
.c_str(), &vv
, -1);
1281 expand_meta(tmp
, NULL
, stack
, oss
);
1284 // this isn't really right, but it'll result in a mangled
1285 // non-existent path that will fail any search list
1289 ceph_abort(); // unreachable
1295 const auto other_opt_iter
= schema
.find(var
);
1296 if (other_opt_iter
!= schema
.end()) {
1297 const Option
&other_opt
= other_opt_iter
->second
;
1298 if (other_opt
.type
== Option::TYPE_STR
) {
1299 // The referenced option is a string, it may need substitution
1300 // before inserting.
1301 Option::value_t
*other_val_ptr
= const_cast<Option::value_t
*>(&(values
.at(other_opt
.name
)));
1302 std::string
*other_opt_val
= boost::get
<std::string
>(other_val_ptr
);
1303 expand_meta(*other_opt_val
, &other_opt
, stack
, oss
);
1304 out
+= *other_opt_val
;
1306 // The referenced option is not a string: retrieve and insert
1307 // its stringized form.
1309 _get_val(other_opt
.name
, &vv
, -1);
1325 // override the original value with the expanded value
1330 void md_config_t::diff(
1331 const md_config_t
*other
,
1332 map
<string
, pair
<string
, string
> > *diff
,
1333 set
<string
> *unknown
)
1335 diff_helper(other
, diff
, unknown
);
1337 void md_config_t::diff(
1338 const md_config_t
*other
,
1339 map
<string
, pair
<string
, string
> > *diff
,
1340 set
<string
> *unknown
, const string
& setting
)
1342 diff_helper(other
, diff
, unknown
, setting
);
1345 void md_config_t::diff_helper(
1346 const md_config_t
*other
,
1347 map
<string
,pair
<string
,string
> > *diff
,
1348 set
<string
> *unknown
, const string
& setting
)
1350 Mutex::Locker
l(lock
);
1352 char local_buf
[4096];
1353 char other_buf
[4096];
1354 for (const auto &i
: schema
) {
1355 const Option
&opt
= i
.second
;
1356 if (!setting
.empty()) {
1357 if (setting
!= opt
.name
) {
1361 memset(local_buf
, 0, sizeof(local_buf
));
1362 memset(other_buf
, 0, sizeof(other_buf
));
1364 char *other_val
= other_buf
;
1365 int err
= other
->get_val(opt
.name
, &other_val
, sizeof(other_buf
));
1367 if (err
== -ENOENT
) {
1368 unknown
->insert(opt
.name
);
1373 char *local_val
= local_buf
;
1374 err
= _get_val(opt
.name
, &local_val
, sizeof(local_buf
));
1378 if (strcmp(local_val
, other_val
))
1379 diff
->insert(make_pair(opt
.name
, make_pair(local_val
, other_val
)));
1380 else if (!setting
.empty()) {
1381 diff
->insert(make_pair(opt
.name
, make_pair(local_val
, other_val
)));
1387 void md_config_t::complain_about_parse_errors(CephContext
*cct
)
1389 ::complain_about_parse_errors(cct
, &parse_errors
);