]> git.proxmox.com Git - ceph.git/blame - ceph/src/common/config.cc
update sources to v12.1.2
[ceph.git] / ceph / src / common / config.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3/*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
7c673cae
FG
15#include "common/ceph_argparse.h"
16#include "common/common_init.h"
17#include "common/config.h"
7c673cae 18#include "include/str_list.h"
7c673cae 19#include "include/stringify.h"
7c673cae
FG
20#include "osd/osd_types.h"
21#include "common/errno.h"
22#include "common/hostname.h"
23
7c673cae 24#include <boost/type_traits.hpp>
7c673cae
FG
25
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.
29 */
30#undef dout
31#undef ldout
32#undef pdout
33#undef derr
34#undef lderr
35#undef generic_dout
36#undef dendl
37
38using std::map;
39using std::list;
7c673cae
FG
40using std::ostringstream;
41using std::pair;
7c673cae
FG
42using std::string;
43
44const 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"
47#endif
48 ;
49
50#define _STR(x) #x
51#define STRINGIFY(x) _STR(x)
52
53int ceph_resolve_file_search(const std::string& filename_list,
54 std::string& result)
55{
56 list<string> ls;
57 get_str_list(filename_list, ls);
58
59 int ret = -ENOENT;
60 list<string>::iterator iter;
61 for (iter = ls.begin(); iter != ls.end(); ++iter) {
62 int fd = ::open(iter->c_str(), O_RDONLY);
63 if (fd < 0) {
64 ret = -errno;
65 continue;
66 }
67 close(fd);
68 result = *iter;
69 return 0;
70 }
71
72 return ret;
73}
74
c07f9fc5
FG
75
76
77md_config_t::md_config_t(bool is_daemon)
78 : cluster(""),
79 lock("md_config_t", true, false)
80{
81 init_subsys();
82
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 << "'"
89 << std::endl;
90 assert(false);
91 }
92 schema.insert({i.name, i});
93 }
94
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.
100 legacy_values = {
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"
7c673cae 105#undef OPTION
7c673cae 106#undef SAFE_OPTION
c07f9fc5 107 };
7c673cae 108
c07f9fc5 109 validate_schema();
7c673cae 110
c07f9fc5
FG
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;
118 } else {
119 default_val = opt.value;
120 }
7c673cae 121
c07f9fc5
FG
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);
126 std::string err;
127 if (opt.pre_validate(def_str, &err) != 0) {
128 std::cerr << "Default value " << opt.name << "=" << *def_str << " is "
129 "invalid: " << err << std::endl;
130
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.
134 assert(false);
135 }
136 }
7c673cae 137
c07f9fc5
FG
138 values[i.first] = default_val;
139 }
7c673cae 140
c07f9fc5
FG
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);
145 auto ptr = i.second;
7c673cae 146
c07f9fc5
FG
147 update_legacy_val(option, ptr);
148 }
149}
150
151/**
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.
154 */
155void md_config_t::validate_schema()
7c673cae 156{
c07f9fc5
FG
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;
163 assert(false);
164 }
165 }
166 }
7c673cae 167
c07f9fc5
FG
168 for (const auto &i : legacy_values) {
169 if (schema.count(i.first) == 0) {
170 std::cerr << "Schema is missing legacy field '" << i.first << "'"
171 << std::endl;
172 assert(false);
173 }
174 }
7c673cae
FG
175}
176
177void md_config_t::init_subsys()
178{
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);
c07f9fc5 183#include "common/subsys.h"
7c673cae
FG
184#undef SUBSYS
185#undef DEFAULT_SUBSYS
186}
187
188md_config_t::~md_config_t()
189{
190}
191
192void md_config_t::add_observer(md_config_obs_t* observer_)
193{
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);
199 }
200}
201
202void md_config_t::remove_observer(md_config_obs_t* observer_)
203{
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++);
209 found_obs = true;
210 }
211 else {
212 ++o;
213 }
214 }
215 assert(found_obs);
216}
217
218int md_config_t::parse_config_files(const char *conf_files,
219 std::ostream *warnings,
220 int flags)
221{
222 Mutex::Locker l(lock);
223
224 if (internal_safe_to_start_threads)
225 return -ENOSYS;
226
227 if (!cluster.size() && !conf_files) {
228 /*
229 * set the cluster name to 'ceph' when neither cluster name nor
230 * configuration file are specified.
231 */
232 cluster = "ceph";
233 }
234
235 if (!conf_files) {
236 const char *c = getenv("CEPH_CONF");
237 if (c) {
238 conf_files = c;
239 }
240 else {
241 if (flags & CINIT_FLAG_NO_DEFAULT_CONFIG_FILE)
242 return 0;
243 conf_files = CEPH_CONF_FILE_DEFAULT;
244 }
245 }
246
247 std::list<std::string> cfl;
248 get_str_list(conf_files, cfl);
249
250 auto p = cfl.begin();
251 while (p != cfl.end()) {
252 // expand $data_dir?
253 string &s = *p;
254 if (s.find("$data_dir") != string::npos) {
255 if (data_dir_option.length()) {
c07f9fc5 256 list<const Option *> stack;
7c673cae
FG
257 expand_meta(s, NULL, stack, warnings);
258 p++;
259 } else {
260 cfl.erase(p++); // ignore this item
261 }
262 } else {
263 ++p;
264 }
265 }
266 return parse_config_files_impl(cfl, warnings);
267}
268
269int md_config_t::parse_config_files_impl(const std::list<std::string> &conf_files,
270 std::ostream *warnings)
271{
272 assert(lock.is_locked());
273
274 // open new conf
275 list<string>::const_iterator c;
276 for (c = conf_files.begin(); c != conf_files.end(); ++c) {
277 cf.clear();
278 string fn = *c;
279 expand_meta(fn, warnings);
280 int ret = cf.parse_file(fn.c_str(), &parse_errors, warnings);
281 if (ret == 0)
282 break;
283 else if (ret != -ENOENT)
284 return ret;
285 }
286 // it must have been all ENOENTs, that's the only way we got here
287 if (c == conf_files.end())
288 return -ENOENT;
289
290 if (cluster.size() == 0) {
291 /*
292 * If cluster name is not set yet, use the prefix of the
293 * basename of configuration file as cluster name.
294 */
295 auto start = c->rfind('/') + 1;
296 auto end = c->find(".conf", start);
297 if (end == c->npos) {
298 /*
299 * If the configuration file does not follow $cluster.conf
300 * convention, we do the last try and assign the cluster to
301 * 'ceph'.
302 */
303 cluster = "ceph";
304 } else {
305 cluster = c->substr(start, end - start);
306 }
307 }
308
309 std::vector <std::string> my_sections;
310 _get_my_sections(my_sections);
c07f9fc5
FG
311 for (const auto &i : schema) {
312 const auto &opt = i.second;
7c673cae
FG
313 std::string val;
314 int ret = _get_val_from_conf_file(my_sections, opt.name, val, false);
315 if (ret == 0) {
316 std::string error_message;
c07f9fc5 317 int r = set_val_impl(val, opt, &error_message);
7c673cae
FG
318 if (warnings != nullptr && (r != 0 || !error_message.empty())) {
319 *warnings << "parse error setting '" << opt.name << "' to '" << val
320 << "'";
321 if (!error_message.empty()) {
322 *warnings << " (" << error_message << ")";
323 }
324 *warnings << std::endl;
325 }
326 }
327 }
328
329 // subsystems?
c07f9fc5 330 for (size_t o = 0; o < subsys.get_num(); o++) {
7c673cae
FG
331 std::string as_option("debug_");
332 as_option += subsys.get_name(o);
333 std::string val;
334 int ret = _get_val_from_conf_file(my_sections, as_option.c_str(), val, false);
335 if (ret == 0) {
336 int log, gather;
337 int r = sscanf(val.c_str(), "%d/%d", &log, &gather);
338 if (r >= 1) {
339 if (r < 2)
340 gather = log;
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);
344 }
345 }
346 }
347
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);
356 }
357 }
358 if (!old_style_section_names.empty()) {
359 ostringstream oss;
360 cerr << "ERROR! old-style section name(s) found: ";
361 string sep;
362 for (std::deque < std::string >::const_iterator os = old_style_section_names.begin();
363 os != old_style_section_names.end(); ++os) {
364 cerr << sep << *os;
365 sep = ", ";
366 }
367 cerr << ". Please use the new style section names that include a period.";
368 }
369 return 0;
370}
371
372void md_config_t::parse_env()
373{
374 Mutex::Locker l(lock);
375 if (internal_safe_to_start_threads)
376 return;
377 if (getenv("CEPH_KEYRING")) {
378 set_val_or_die("keyring", getenv("CEPH_KEYRING"));
379 }
380}
381
382void md_config_t::show_config(std::ostream& out)
383{
384 Mutex::Locker l(lock);
385 _show_config(&out, NULL);
386}
387
388void md_config_t::show_config(Formatter *f)
389{
390 Mutex::Locker l(lock);
391 _show_config(NULL, f);
392}
393
394void md_config_t::_show_config(std::ostream *out, Formatter *f)
395{
396 if (out) {
397 *out << "name = " << name << std::endl;
398 *out << "cluster = " << cluster << std::endl;
399 }
400 if (f) {
401 f->dump_string("name", stringify(name));
402 f->dump_string("cluster", cluster);
403 }
c07f9fc5 404 for (size_t o = 0; o < subsys.get_num(); o++) {
7c673cae
FG
405 if (out)
406 *out << "debug_" << subsys.get_name(o)
407 << " = " << subsys.get_log_level(o)
408 << "/" << subsys.get_gather_level(o) << std::endl;
409 if (f) {
410 ostringstream ss;
411 std::string debug_name = "debug_";
412 debug_name += subsys.get_name(o);
413 ss << subsys.get_log_level(o)
414 << "/" << subsys.get_gather_level(o);
415 f->dump_string(debug_name.c_str(), ss.str());
416 }
417 }
c07f9fc5
FG
418 for (const auto& i: schema) {
419 const Option &opt = i.second;
7c673cae
FG
420 char *buf;
421 _get_val(opt.name, &buf, -1);
422 if (out)
423 *out << opt.name << " = " << buf << std::endl;
424 if (f)
c07f9fc5 425 f->dump_string(opt.name.c_str(), buf);
7c673cae
FG
426 free(buf);
427 }
428}
429
430int md_config_t::parse_argv(std::vector<const char*>& args)
431{
432 Mutex::Locker l(lock);
433 if (internal_safe_to_start_threads) {
434 return -ENOSYS;
435 }
436
437 bool show_config = false;
438 bool show_config_value = false;
439 string show_config_value_arg;
440
441 // In this function, don't change any parts of the configuration directly.
442 // Instead, use set_val to set them. This will allow us to send the proper
443 // observer notifications later.
444 std::string val;
445 for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
446 if (strcmp(*i, "--") == 0) {
447 /* Normally we would use ceph_argparse_double_dash. However, in this
448 * function we *don't* want to remove the double dash, because later
449 * argument parses will still need to see it. */
450 break;
451 }
452 else if (ceph_argparse_flag(args, i, "--show_conf", (char*)NULL)) {
453 cerr << cf << std::endl;
454 _exit(0);
455 }
456 else if (ceph_argparse_flag(args, i, "--show_config", (char*)NULL)) {
457 show_config = true;
458 }
459 else if (ceph_argparse_witharg(args, i, &val, "--show_config_value", (char*)NULL)) {
460 show_config_value = true;
461 show_config_value_arg = val;
462 }
463 else if (ceph_argparse_flag(args, i, "--foreground", "-f", (char*)NULL)) {
464 set_val_or_die("daemonize", "false");
465 }
466 else if (ceph_argparse_flag(args, i, "-d", (char*)NULL)) {
467 set_val_or_die("daemonize", "false");
468 set_val_or_die("log_file", "");
469 set_val_or_die("log_to_stderr", "true");
470 set_val_or_die("err_to_stderr", "true");
471 set_val_or_die("log_to_syslog", "false");
472 }
473 // Some stuff that we wanted to give universal single-character options for
474 // Careful: you can burn through the alphabet pretty quickly by adding
475 // to this list.
476 else if (ceph_argparse_witharg(args, i, &val, "--monmap", "-M", (char*)NULL)) {
477 set_val_or_die("monmap", val.c_str());
478 }
479 else if (ceph_argparse_witharg(args, i, &val, "--mon_host", "-m", (char*)NULL)) {
480 set_val_or_die("mon_host", val.c_str());
481 }
482 else if (ceph_argparse_witharg(args, i, &val, "--bind", (char*)NULL)) {
483 set_val_or_die("public_addr", val.c_str());
484 }
485 else if (ceph_argparse_witharg(args, i, &val, "--keyfile", "-K", (char*)NULL)) {
486 set_val_or_die("keyfile", val.c_str());
487 }
488 else if (ceph_argparse_witharg(args, i, &val, "--keyring", "-k", (char*)NULL)) {
489 set_val_or_die("keyring", val.c_str());
490 }
491 else if (ceph_argparse_witharg(args, i, &val, "--client_mountpoint", "-r", (char*)NULL)) {
492 set_val_or_die("client_mountpoint", val.c_str());
493 }
494 else {
495 parse_option(args, i, NULL);
496 }
497 }
498
499 if (show_config) {
500 expand_all_meta();
501 _show_config(&cout, NULL);
502 _exit(0);
503 }
504
505 if (show_config_value) {
506 char *buf = 0;
507 int r = _get_val(show_config_value_arg.c_str(), &buf, -1);
508 if (r < 0) {
509 if (r == -ENOENT)
510 std::cerr << "failed to get config option '" <<
511 show_config_value_arg << "': option not found" << std::endl;
512 else
513 std::cerr << "failed to get config option '" <<
514 show_config_value_arg << "': " << cpp_strerror(r) << std::endl;
515 _exit(1);
516 }
517 string s = buf;
518 expand_meta(s, &std::cerr);
519 std::cout << s << std::endl;
520 _exit(0);
521 }
522
523 return 0;
524}
525
526int md_config_t::parse_option(std::vector<const char*>& args,
527 std::vector<const char*>::iterator& i,
528 ostream *oss)
529{
530 int ret = 0;
c07f9fc5 531 size_t o = 0;
7c673cae
FG
532 std::string val;
533
534 // subsystems?
535 for (o = 0; o < subsys.get_num(); o++) {
536 std::string as_option("--");
537 as_option += "debug_";
538 as_option += subsys.get_name(o);
539 if (ceph_argparse_witharg(args, i, &val,
540 as_option.c_str(), (char*)NULL)) {
541 int log, gather;
542 int r = sscanf(val.c_str(), "%d/%d", &log, &gather);
543 if (r >= 1) {
544 if (r < 2)
545 gather = log;
546 // cout << "subsys " << subsys.get_name(o) << " log " << log << " gather " << gather << std::endl;
547 subsys.set_log_level(o, log);
548 subsys.set_gather_level(o, gather);
549 if (oss)
550 *oss << "debug_" << subsys.get_name(o) << "=" << log << "/" << gather << " ";
551 }
552 break;
553 }
554 }
555 if (o < subsys.get_num()) {
556 return ret;
557 }
558
c07f9fc5 559 std::string option_name;
7c673cae
FG
560 std::string error_message;
561 o = 0;
c07f9fc5
FG
562 for (const auto& opt_iter: schema) {
563 const Option &opt = opt_iter.second;
7c673cae 564 ostringstream err;
7c673cae 565 std::string as_option("--");
c07f9fc5
FG
566 as_option += opt.name;
567 option_name = opt.name;
568 if (opt.type == Option::TYPE_BOOL) {
7c673cae
FG
569 int res;
570 if (ceph_argparse_binary_flag(args, i, &res, oss, as_option.c_str(),
571 (char*)NULL)) {
572 if (res == 0)
573 ret = set_val_impl("false", opt, &error_message);
574 else if (res == 1)
575 ret = set_val_impl("true", opt, &error_message);
576 else
577 ret = res;
578 break;
579 } else {
580 std::string no("--no-");
c07f9fc5 581 no += opt.name;
7c673cae
FG
582 if (ceph_argparse_flag(args, i, no.c_str(), (char*)NULL)) {
583 ret = set_val_impl("false", opt, &error_message);
584 break;
585 }
586 }
587 } else if (ceph_argparse_witharg(args, i, &val, err,
588 as_option.c_str(), (char*)NULL)) {
589 if (!err.str().empty()) {
590 error_message = err.str();
591 ret = -EINVAL;
592 break;
593 }
c07f9fc5
FG
594 if (oss && ((!opt.is_safe()) &&
595 (observers.find(opt.name) == observers.end()))) {
596 *oss << "You cannot change " << opt.name << " using injectargs.\n";
7c673cae
FG
597 return -ENOSYS;
598 }
599 ret = set_val_impl(val, opt, &error_message);
600 break;
601 }
602 ++o;
603 }
604
605 if (ret != 0 || !error_message.empty()) {
c07f9fc5 606 assert(!option_name.empty());
7c673cae
FG
607 if (oss) {
608 *oss << "Parse error setting " << option_name << " to '"
609 << val << "' using injectargs";
610 if (!error_message.empty()) {
611 *oss << " (" << error_message << ")";
612 }
613 *oss << ".\n";
614 } else {
615 cerr << "parse error setting '" << option_name << "' to '"
616 << val << "'";
617 if (!error_message.empty()) {
618 cerr << " (" << error_message << ")";
619 }
620 cerr << "\n" << std::endl;
621 }
622 }
623
c07f9fc5 624 if (o == schema.size()) {
7c673cae
FG
625 // ignore
626 ++i;
627 }
628 return ret;
629}
630
631int md_config_t::parse_injectargs(std::vector<const char*>& args,
632 std::ostream *oss)
633{
634 assert(lock.is_locked());
635 int ret = 0;
636 for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
637 int r = parse_option(args, i, oss);
638 if (r < 0)
639 ret = r;
640 }
641 return ret;
642}
643
644void md_config_t::apply_changes(std::ostream *oss)
645{
646 Mutex::Locker l(lock);
647 /*
648 * apply changes until the cluster name is assigned
649 */
650 if (cluster.size())
651 _apply_changes(oss);
652}
653
654bool md_config_t::_internal_field(const string& s)
655{
656 if (s == "internal_safe_to_start_threads")
657 return true;
658 return false;
659}
660
661void md_config_t::_apply_changes(std::ostream *oss)
662{
663 /* Maps observers to the configuration options that they care about which
664 * have changed. */
665 typedef std::map < md_config_obs_t*, std::set <std::string> > rev_obs_map_t;
666
667 expand_all_meta();
668
c07f9fc5
FG
669 // expand_all_meta could have modified anything. Copy it all out again.
670 for (const auto &i : legacy_values) {
671 const auto &name = i.first;
672 const auto &option = schema.at(name);
673 auto ptr = i.second;
674
675 update_legacy_val(option, ptr);
676 }
677
7c673cae
FG
678 // create the reverse observer mapping, mapping observers to the set of
679 // changed keys that they'll get.
680 rev_obs_map_t robs;
681 std::set <std::string> empty_set;
682 char buf[128];
683 char *bufptr = (char*)buf;
684 for (changed_set_t::const_iterator c = changed.begin();
685 c != changed.end(); ++c) {
686 const std::string &key(*c);
687 pair < obs_map_t::iterator, obs_map_t::iterator >
688 range(observers.equal_range(key));
689 if ((oss) &&
690 (!_get_val(key.c_str(), &bufptr, sizeof(buf))) &&
691 !_internal_field(key)) {
692 (*oss) << key << " = '" << buf << "' ";
693 if (range.first == range.second) {
694 (*oss) << "(not observed, change may require restart) ";
695 }
696 }
697 for (obs_map_t::iterator r = range.first; r != range.second; ++r) {
698 rev_obs_map_t::value_type robs_val(r->second, empty_set);
699 pair < rev_obs_map_t::iterator, bool > robs_ret(robs.insert(robs_val));
700 std::set <std::string> &keys(robs_ret.first->second);
701 keys.insert(key);
702 }
703 }
704
705 changed.clear();
706
707 // Make any pending observer callbacks
708 for (rev_obs_map_t::const_iterator r = robs.begin(); r != robs.end(); ++r) {
709 md_config_obs_t *obs = r->first;
710 obs->handle_conf_change(this, r->second);
711 }
712
713}
714
715void md_config_t::call_all_observers()
716{
717 std::map<md_config_obs_t*,std::set<std::string> > obs;
718 {
719 Mutex::Locker l(lock);
720
721 expand_all_meta();
722
224ce89b 723 for (auto r = observers.begin(); r != observers.end(); ++r) {
7c673cae
FG
724 obs[r->second].insert(r->first);
725 }
726 }
224ce89b 727 for (auto p = obs.begin();
7c673cae
FG
728 p != obs.end();
729 ++p) {
730 p->first->handle_conf_change(this, p->second);
731 }
732}
733
734int md_config_t::injectargs(const std::string& s, std::ostream *oss)
735{
736 int ret;
737 Mutex::Locker l(lock);
738 char b[s.length()+1];
739 strcpy(b, s.c_str());
740 std::vector<const char*> nargs;
741 char *p = b;
742 while (*p) {
743 nargs.push_back(p);
744 while (*p && *p != ' ') p++;
745 if (!*p)
746 break;
747 *p++ = 0;
748 while (*p && *p == ' ') p++;
749 }
750 ret = parse_injectargs(nargs, oss);
751 if (!nargs.empty()) {
752 *oss << " failed to parse arguments: ";
753 std::string prefix;
754 for (std::vector<const char*>::const_iterator i = nargs.begin();
755 i != nargs.end(); ++i) {
756 *oss << prefix << *i;
757 prefix = ",";
758 }
759 *oss << "\n";
760 ret = -EINVAL;
761 }
762 _apply_changes(oss);
763 return ret;
764}
765
c07f9fc5
FG
766void md_config_t::set_val_or_die(const std::string &key,
767 const std::string &val,
768 bool meta)
7c673cae 769{
c07f9fc5
FG
770 std::stringstream err;
771 int ret = set_val(key, val, meta, &err);
772 if (ret != 0) {
773 std::cerr << "set_val_or_die(" << key << "): " << err.str();
7c673cae 774 }
c07f9fc5 775 assert(ret == 0);
7c673cae
FG
776}
777
c07f9fc5
FG
778int md_config_t::set_val(const std::string &key, const char *val,
779 bool meta, std::stringstream *err_ss)
7c673cae
FG
780{
781 Mutex::Locker l(lock);
c07f9fc5
FG
782 if (key.empty()) {
783 if (err_ss) *err_ss << "No key specified";
7c673cae 784 return -EINVAL;
c07f9fc5
FG
785 }
786 if (!val) {
7c673cae 787 return -EINVAL;
c07f9fc5 788 }
7c673cae
FG
789
790 std::string v(val);
791 if (meta)
792 expand_meta(v, &std::cerr);
793
794 string k(ConfFile::normalize_key_name(key));
795
796 // subsystems?
797 if (strncmp(k.c_str(), "debug_", 6) == 0) {
c07f9fc5 798 for (size_t o = 0; o < subsys.get_num(); o++) {
7c673cae
FG
799 std::string as_option = "debug_" + subsys.get_name(o);
800 if (k == as_option) {
801 int log, gather;
802 int r = sscanf(v.c_str(), "%d/%d", &log, &gather);
803 if (r >= 1) {
c07f9fc5 804 if (r < 2) {
7c673cae 805 gather = log;
c07f9fc5 806 }
7c673cae
FG
807 subsys.set_log_level(o, log);
808 subsys.set_gather_level(o, gather);
c07f9fc5 809 if (err_ss) *err_ss << "Set " << k << " to " << log << "/" << gather;
7c673cae
FG
810 return 0;
811 }
c07f9fc5
FG
812 if (err_ss) {
813 *err_ss << "Invalid debug level, should be <int> or <int>/<int>";
814 }
7c673cae
FG
815 return -EINVAL;
816 }
817 }
818 }
819
c07f9fc5
FG
820 const auto &opt_iter = schema.find(k);
821 if (opt_iter != schema.end()) {
822 const Option &opt = opt_iter->second;
823 if ((!opt.is_safe()) && internal_safe_to_start_threads) {
7c673cae 824 // If threads have been started and the option is not thread safe
c07f9fc5 825 if (observers.find(opt.name) == observers.end()) {
7c673cae
FG
826 // And there is no observer to safely change it...
827 // You lose.
c07f9fc5
FG
828 if (err_ss) *err_ss << "Configuration option '" << key << "' may "
829 "not be modified at runtime";
7c673cae
FG
830 return -ENOSYS;
831 }
832 }
833
834 std::string error_message;
835 int r = set_val_impl(v, opt, &error_message);
c07f9fc5
FG
836 if (r == 0) {
837 if (err_ss) *err_ss << "Set " << opt.name << " to " << v;
838 } else {
839 if (err_ss) *err_ss << error_message;
840 }
7c673cae
FG
841 return r;
842 }
843
c07f9fc5 844 if (err_ss) *err_ss << "Configuration option not found: '" << key << "'";
7c673cae
FG
845 return -ENOENT;
846}
847
848
c07f9fc5 849int md_config_t::get_val(const std::string &key, char **buf, int len) const
7c673cae
FG
850{
851 Mutex::Locker l(lock);
852 return _get_val(key, buf,len);
853}
854
c07f9fc5 855Option::value_t md_config_t::get_val_generic(const std::string &key) const
7c673cae
FG
856{
857 Mutex::Locker l(lock);
858 return _get_val(key);
859}
860
c07f9fc5 861Option::value_t md_config_t::_get_val(const std::string &key) const
7c673cae
FG
862{
863 assert(lock.is_locked());
864
c07f9fc5
FG
865 if (key.empty()) {
866 return Option::value_t(boost::blank());
867 }
7c673cae
FG
868
869 // In key names, leading and trailing whitespace are not significant.
870 string k(ConfFile::normalize_key_name(key));
871
c07f9fc5
FG
872 const auto &opt_iter = schema.find(k);
873 if (opt_iter != schema.end()) {
874 // Using .at() is safe because all keys in the schema always have
875 // entries in ::values
876 return values.at(k);
877 } else {
878 return Option::value_t(boost::blank());
7c673cae 879 }
7c673cae
FG
880}
881
c07f9fc5 882int md_config_t::_get_val(const std::string &key, std::string *value) const {
7c673cae
FG
883 assert(lock.is_locked());
884
885 std::string normalized_key(ConfFile::normalize_key_name(key));
c07f9fc5
FG
886 Option::value_t config_value = _get_val(normalized_key.c_str());
887 if (!boost::get<boost::blank>(&config_value)) {
7c673cae
FG
888 ostringstream oss;
889 if (bool *flag = boost::get<bool>(&config_value)) {
890 oss << (*flag ? "true" : "false");
224ce89b 891 } else if (double *dp = boost::get<double>(&config_value)) {
c07f9fc5 892 oss << std::fixed << *dp;
7c673cae
FG
893 } else {
894 oss << config_value;
895 }
896 *value = oss.str();
897 return 0;
898 }
899 return -ENOENT;
900}
901
c07f9fc5 902int md_config_t::_get_val(const std::string &key, char **buf, int len) const
7c673cae
FG
903{
904 assert(lock.is_locked());
905
c07f9fc5 906 if (key.empty())
7c673cae
FG
907 return -EINVAL;
908
224ce89b 909 string val ;
c07f9fc5 910 if (_get_val(key, &val) == 0) {
224ce89b 911 int l = val.length() + 1;
7c673cae
FG
912 if (len == -1) {
913 *buf = (char*)malloc(l);
914 if (!*buf)
915 return -ENOMEM;
224ce89b 916 strncpy(*buf, val.c_str(), l);
7c673cae
FG
917 return 0;
918 }
224ce89b 919 snprintf(*buf, len, "%s", val.c_str());
7c673cae
FG
920 return (l > len) ? -ENAMETOOLONG : 0;
921 }
922
224ce89b 923 string k(ConfFile::normalize_key_name(key));
7c673cae 924 // subsys?
c07f9fc5 925 for (size_t o = 0; o < subsys.get_num(); o++) {
7c673cae
FG
926 std::string as_option = "debug_" + subsys.get_name(o);
927 if (k == as_option) {
928 if (len == -1) {
929 *buf = (char*)malloc(20);
930 len = 20;
931 }
932 int l = snprintf(*buf, len, "%d/%d", subsys.get_log_level(o), subsys.get_gather_level(o));
933 return (l == len) ? -ENAMETOOLONG : 0;
934 }
935 }
936
937 // couldn't find a configuration option with key 'k'
938 return -ENOENT;
939}
940
941void md_config_t::get_all_keys(std::vector<std::string> *keys) const {
942 const std::string negative_flag_prefix("no_");
943
944 keys->clear();
c07f9fc5
FG
945 keys->reserve(schema.size());
946 for (const auto &i: schema) {
947 const Option &opt = i.second;
7c673cae 948 keys->push_back(opt.name);
c07f9fc5 949 if (opt.type == Option::TYPE_BOOL) {
7c673cae
FG
950 keys->push_back(negative_flag_prefix + opt.name);
951 }
952 }
c07f9fc5 953 for (size_t i = 0; i < subsys.get_num(); ++i) {
7c673cae
FG
954 keys->push_back("debug_" + subsys.get_name(i));
955 }
956}
957
958/* The order of the sections here is important. The first section in the
959 * vector is the "highest priority" section; if we find it there, we'll stop
960 * looking. The lowest priority section is the one we look in only if all
961 * others had nothing. This should always be the global section.
962 */
963void md_config_t::get_my_sections(std::vector <std::string> &sections) const
964{
965 Mutex::Locker l(lock);
966 _get_my_sections(sections);
967}
968
969void md_config_t::_get_my_sections(std::vector <std::string> &sections) const
970{
971 assert(lock.is_locked());
972 sections.push_back(name.to_str());
973
974 sections.push_back(name.get_type_name());
975
976 sections.push_back("global");
977}
978
979// Return a list of all sections
980int md_config_t::get_all_sections(std::vector <std::string> &sections) const
981{
982 Mutex::Locker l(lock);
983 for (ConfFile::const_section_iter_t s = cf.sections_begin();
984 s != cf.sections_end(); ++s) {
985 sections.push_back(s->first);
986 }
987 return 0;
988}
989
990int md_config_t::get_val_from_conf_file(const std::vector <std::string> &sections,
c07f9fc5 991 const std::string &key, std::string &out, bool emeta) const
7c673cae
FG
992{
993 Mutex::Locker l(lock);
994 return _get_val_from_conf_file(sections, key, out, emeta);
995}
996
997int md_config_t::_get_val_from_conf_file(const std::vector <std::string> &sections,
c07f9fc5 998 const std::string &key, std::string &out, bool emeta) const
7c673cae
FG
999{
1000 assert(lock.is_locked());
1001 std::vector <std::string>::const_iterator s = sections.begin();
1002 std::vector <std::string>::const_iterator s_end = sections.end();
1003 for (; s != s_end; ++s) {
1004 int ret = cf.read(s->c_str(), key, out);
1005 if (ret == 0) {
1006 if (emeta)
1007 expand_meta(out, &std::cerr);
1008 return 0;
1009 }
1010 else if (ret != -ENOENT)
1011 return ret;
1012 }
1013 return -ENOENT;
1014}
1015
c07f9fc5 1016int md_config_t::set_val_impl(const std::string &raw_val, const Option &opt,
7c673cae
FG
1017 std::string *error_message)
1018{
1019 assert(lock.is_locked());
7c673cae 1020
c07f9fc5 1021 std::string val = raw_val;
7c673cae 1022
c07f9fc5
FG
1023 int r = opt.pre_validate(&val, error_message);
1024 if (r != 0) {
1025 return r;
1026 }
7c673cae 1027
c07f9fc5
FG
1028 Option::value_t new_value;
1029 if (opt.type == Option::TYPE_INT) {
1030 int64_t f = strict_si_cast<int64_t>(val.c_str(), error_message);
1031 if (!error_message->empty()) {
1032 return -EINVAL;
1033 }
1034 new_value = f;
1035 } else if (opt.type == Option::TYPE_UINT) {
1036 uint64_t f = strict_si_cast<uint64_t>(val.c_str(), error_message);
1037 if (!error_message->empty()) {
1038 return -EINVAL;
1039 }
1040 new_value = f;
1041 } else if (opt.type == Option::TYPE_STR) {
1042 new_value = val;
1043 } else if (opt.type == Option::TYPE_FLOAT) {
1044 double f = strict_strtod(val.c_str(), error_message);
1045 if (!error_message->empty()) {
1046 return -EINVAL;
1047 } else {
1048 new_value = f;
1049 }
1050 } else if (opt.type == Option::TYPE_BOOL) {
1051 if (strcasecmp(val.c_str(), "false") == 0) {
1052 new_value = false;
1053 } else if (strcasecmp(val.c_str(), "true") == 0) {
1054 new_value = true;
1055 } else {
1056 int b = strict_strtol(val.c_str(), 10, error_message);
1057 if (!error_message->empty()) {
1058 return -EINVAL;
1059 }
1060 new_value = !!b;
1061 }
1062 } else if (opt.type == Option::TYPE_ADDR) {
1063 entity_addr_t addr;
1064 if (!addr.parse(val.c_str())){
1065 return -EINVAL;
1066 }
1067 new_value = addr;
1068 } else if (opt.type == Option::TYPE_UUID) {
1069 uuid_d uuid;
1070 if (!uuid.parse(val.c_str())) {
1071 return -EINVAL;
1072 }
1073 new_value = uuid;
1074 } else {
1075 ceph_abort();
7c673cae 1076 }
7c673cae 1077
c07f9fc5
FG
1078 r = opt.validate(new_value, error_message);
1079 if (r != 0) {
1080 return r;
7c673cae 1081 }
7c673cae 1082
7c673cae 1083
c07f9fc5
FG
1084 // Apply the value to its entry in the `values` map
1085 values[opt.name] = new_value;
7c673cae 1086
c07f9fc5
FG
1087 // Apply the value to its legacy field, if it has one
1088 auto legacy_ptr_iter = legacy_values.find(std::string(opt.name));
1089 if (legacy_ptr_iter != legacy_values.end()) {
1090 update_legacy_val(opt, legacy_ptr_iter->second);
7c673cae
FG
1091 }
1092
c07f9fc5
FG
1093 changed.insert(opt.name);
1094 return 0;
1095}
7c673cae 1096
c07f9fc5
FG
1097/**
1098 * Handles assigning from a variant-of-types to a variant-of-pointers-to-types
1099 */
1100class assign_visitor : public boost::static_visitor<>
1101{
1102 md_config_t *conf;
1103 Option::value_t val;
1104 public:
7c673cae 1105
c07f9fc5
FG
1106 assign_visitor(md_config_t *conf_, Option::value_t val_)
1107 : conf(conf_), val(val_)
1108 {}
1109
1110 template <typename T>
1111 void operator()( T md_config_t::* ptr) const
1112 {
1113 T *member = const_cast<T *>(&(conf->*(boost::get<const T md_config_t::*>(ptr))));
1114
1115 *member = boost::get<T>(val);
7c673cae
FG
1116 }
1117};
1118
c07f9fc5
FG
1119void md_config_t::update_legacy_val(const Option &opt,
1120 md_config_t::member_ptr_t member_ptr)
7c673cae 1121{
c07f9fc5
FG
1122 if (boost::get<boost::blank>(&values.at(opt.name))) {
1123 // This shouldn't happen, but if it does then just don't even
1124 // try to assign to the legacy field.
1125 return;
1126 }
1127
1128 boost::apply_visitor(assign_visitor(this, values.at(opt.name)), member_ptr);
7c673cae
FG
1129}
1130
c07f9fc5 1131
7c673cae
FG
1132static const char *CONF_METAVARIABLES[] = {
1133 "data_dir", // put this first: it may contain some of the others
1134 "cluster", "type", "name", "host", "num", "id", "pid", "cctid"
1135};
1136static const int NUM_CONF_METAVARIABLES =
1137 (sizeof(CONF_METAVARIABLES) / sizeof(CONF_METAVARIABLES[0]));
1138
1139void md_config_t::expand_all_meta()
1140{
1141 // Expand all metavariables
1142 ostringstream oss;
c07f9fc5
FG
1143 for (const auto &i : schema) {
1144 const Option &opt = i.second;
1145
1146 if (opt.type == Option::TYPE_STR) {
1147 list<const Option*> stack;
1148 std::string *str = boost::get<std::string>(&(values.at(opt.name)));
1149 assert(str != nullptr); // Non-string values should never get in
7c673cae
FG
1150 expand_meta(*str, &opt, stack, &oss);
1151 }
1152 }
1153 cerr << oss.str();
1154}
1155
c07f9fc5 1156bool md_config_t::expand_meta(std::string &val,
7c673cae
FG
1157 std::ostream *oss) const
1158{
c07f9fc5
FG
1159 list<const Option*> stack;
1160 return expand_meta(val, NULL, stack, oss);
7c673cae
FG
1161}
1162
1163bool md_config_t::expand_meta(std::string &origval,
c07f9fc5
FG
1164 const Option *opt,
1165 std::list<const Option *> stack,
7c673cae
FG
1166 std::ostream *oss) const
1167{
1168 assert(lock.is_locked());
1169
1170 // no $ means no variable expansion is necessary
1171 if (origval.find("$") == string::npos)
1172 return false;
1173
1174 // ignore an expansion loop and create a human readable
1175 // message about it
1176 if (opt) {
c07f9fc5
FG
1177 for (const auto stack_ptr : stack) {
1178 if (opt->name == stack_ptr->name) {
7c673cae
FG
1179 *oss << "variable expansion loop at "
1180 << opt->name << "=" << origval << std::endl;
1181 *oss << "expansion stack: " << std::endl;
c07f9fc5
FG
1182 for (const auto j : stack) {
1183 std::string val;
1184 _get_val(j->name, &val);
1185 *oss << j->name << "=" << val << std::endl;
7c673cae
FG
1186 }
1187 return false;
1188 }
1189 }
7c673cae 1190
7c673cae 1191 stack.push_front(opt);
c07f9fc5 1192 }
7c673cae
FG
1193
1194 bool found_meta = false;
1195 string out;
1196 string val = origval;
1197 for (string::size_type s = 0; s < val.size(); ) {
1198 if (val[s] != '$') {
1199 out += val[s++];
1200 continue;
1201 }
1202
1203 // try to parse the variable name into var, either \$\{(.+)\} or
1204 // \$[a-z\_]+
1205 const char *valid_chars = "abcdefghijklmnopqrstuvwxyz_";
1206 string var;
1207 size_t endpos = 0;
1208 if (val[s+1] == '{') {
1209 // ...${foo_bar}...
1210 endpos = val.find_first_not_of(valid_chars, s+2);
1211 if (endpos != std::string::npos &&
1212 val[endpos] == '}') {
1213 var = val.substr(s+2, endpos-s-2);
1214 endpos++;
1215 }
1216 } else {
1217 // ...$foo...
1218 endpos = val.find_first_not_of(valid_chars, s+1);
1219 if (endpos != std::string::npos)
1220 var = val.substr(s+1, endpos-s-1);
1221 else
1222 var = val.substr(s+1);
1223 }
1224
1225 bool expanded = false;
1226 if (var.length()) {
1227 // special metavariable?
1228 for (int i = 0; i < NUM_CONF_METAVARIABLES; ++i) {
1229 if (var != CONF_METAVARIABLES[i])
1230 continue;
1231 //cout << " meta match of " << var << " " << CONF_METAVARIABLES[i] << std::endl;
1232 if (var == "type")
1233 out += name.get_type_name();
1234 else if (var == "cluster")
1235 out += cluster;
1236 else if (var == "name")
1237 out += name.to_cstr();
1238 else if (var == "host")
1239 {
1240 if (host == "")
1241 out += ceph_get_short_hostname();
1242 else
1243 out += host;
1244 }
1245 else if (var == "num")
1246 out += name.get_id().c_str();
1247 else if (var == "id")
1248 out += name.get_id().c_str();
1249 else if (var == "pid")
1250 out += stringify(getpid());
1251 else if (var == "cctid")
1252 out += stringify((unsigned long long)this);
1253 else if (var == "data_dir") {
1254 if (data_dir_option.length()) {
1255 char *vv = NULL;
1256 _get_val(data_dir_option.c_str(), &vv, -1);
1257 string tmp = vv;
1258 free(vv);
1259 expand_meta(tmp, NULL, stack, oss);
1260 out += tmp;
1261 } else {
1262 // this isn't really right, but it'll result in a mangled
1263 // non-existent path that will fail any search list
1264 out += "$data_dir";
1265 }
1266 } else
1267 ceph_abort(); // unreachable
1268 expanded = true;
1269 }
1270
1271 if (!expanded) {
1272 // config option?
c07f9fc5
FG
1273 const auto other_opt_iter = schema.find(var);
1274 if (other_opt_iter != schema.end()) {
1275 const Option &other_opt = other_opt_iter->second;
1276 if (other_opt.type == Option::TYPE_STR) {
1277 // The referenced option is a string, it may need substitution
1278 // before inserting.
1279 Option::value_t *other_val_ptr = const_cast<Option::value_t*>(&(values.at(other_opt.name)));
1280 std::string *other_opt_val = boost::get<std::string>(other_val_ptr);
1281 expand_meta(*other_opt_val, &other_opt, stack, oss);
1282 out += *other_opt_val;
1283 } else {
1284 // The referenced option is not a string: retrieve and insert
1285 // its stringized form.
1286 char *vv = NULL;
1287 _get_val(other_opt.name, &vv, -1);
1288 out += vv;
1289 free(vv);
1290 }
1291 expanded = true;
7c673cae
FG
1292 }
1293 }
1294 }
1295
1296 if (expanded) {
1297 found_meta = true;
1298 s = endpos;
1299 } else {
1300 out += val[s++];
1301 }
1302 }
1303 // override the original value with the expanded value
1304 origval = out;
1305 return found_meta;
1306}
1307
1308void md_config_t::diff(
31f18b77
FG
1309 const md_config_t *other,
1310 map<string, pair<string, string> > *diff,
1311 set<string> *unknown)
1312{
1313 diff_helper(other, diff, unknown);
1314}
1315void md_config_t::diff(
1316 const md_config_t *other,
1317 map<string, pair<string, string> > *diff,
1318 set<string> *unknown, const string& setting)
1319{
1320 diff_helper(other, diff, unknown, setting);
1321}
1322
1323void md_config_t::diff_helper(
7c673cae
FG
1324 const md_config_t *other,
1325 map<string,pair<string,string> > *diff,
31f18b77 1326 set<string> *unknown, const string& setting)
7c673cae
FG
1327{
1328 Mutex::Locker l(lock);
1329
1330 char local_buf[4096];
1331 char other_buf[4096];
c07f9fc5
FG
1332 for (const auto &i : schema) {
1333 const Option &opt = i.second;
31f18b77
FG
1334 if (!setting.empty()) {
1335 if (setting != opt.name) {
1336 continue;
1337 }
1338 }
7c673cae
FG
1339 memset(local_buf, 0, sizeof(local_buf));
1340 memset(other_buf, 0, sizeof(other_buf));
1341
1342 char *other_val = other_buf;
1343 int err = other->get_val(opt.name, &other_val, sizeof(other_buf));
1344 if (err < 0) {
1345 if (err == -ENOENT) {
1346 unknown->insert(opt.name);
1347 }
1348 continue;
1349 }
1350
1351 char *local_val = local_buf;
1352 err = _get_val(opt.name, &local_val, sizeof(local_buf));
1353 if (err != 0)
1354 continue;
1355
1356 if (strcmp(local_val, other_val))
1357 diff->insert(make_pair(opt.name, make_pair(local_val, other_val)));
31f18b77
FG
1358 else if (!setting.empty()) {
1359 diff->insert(make_pair(opt.name, make_pair(local_val, other_val)));
1360 break;
1361 }
7c673cae
FG
1362 }
1363}
1364
1365void md_config_t::complain_about_parse_errors(CephContext *cct)
1366{
1367 ::complain_about_parse_errors(cct, &parse_errors);
1368}
1369