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