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