]> git.proxmox.com Git - ceph.git/blob - ceph/src/common/config.cc
8c026f89a9ffdcdd789dc39a88953b15ed5233c2
[ceph.git] / ceph / src / common / config.cc
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 "common/ceph_argparse.h"
16 #include "common/common_init.h"
17 #include "common/config.h"
18 #include "common/config_validators.h"
19 #include "include/str_list.h"
20 #include "include/stringify.h"
21 #include "osd/osd_types.h"
22 #include "common/errno.h"
23 #include "common/hostname.h"
24
25 #include <boost/type_traits.hpp>
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
39 using std::map;
40 using std::list;
41 using std::ostringstream;
42 using std::pair;
43 using std::string;
44
45 const 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
54 int 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) \
78 struct 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
91 namespace {
92
93 template <typename T>
94 typename 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
99 template <typename T>
100 typename 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
112 md_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
173 void 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
190 md_config_t::~md_config_t()
191 {
192 }
193
194 void 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
204 void 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
220 int 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
271 int 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
373 void 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
383 void md_config_t::show_config(std::ostream& out)
384 {
385 Mutex::Locker l(lock);
386 _show_config(&out, NULL);
387 }
388
389 void md_config_t::show_config(Formatter *f)
390 {
391 Mutex::Locker l(lock);
392 _show_config(NULL, f);
393 }
394
395 void 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
430 int 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
526 int 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()) {
606 if (oss) {
607 *oss << "Parse error setting " << option_name << " to '"
608 << val << "' using injectargs";
609 if (!error_message.empty()) {
610 *oss << " (" << error_message << ")";
611 }
612 *oss << ".\n";
613 } else {
614 cerr << "parse error setting '" << option_name << "' to '"
615 << val << "'";
616 if (!error_message.empty()) {
617 cerr << " (" << error_message << ")";
618 }
619 cerr << "\n" << std::endl;
620 }
621 }
622
623 if (o == (int)config_options->size()) {
624 // ignore
625 ++i;
626 }
627 return ret;
628 }
629
630 int md_config_t::parse_injectargs(std::vector<const char*>& args,
631 std::ostream *oss)
632 {
633 assert(lock.is_locked());
634 int ret = 0;
635 for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
636 int r = parse_option(args, i, oss);
637 if (r < 0)
638 ret = r;
639 }
640 return ret;
641 }
642
643 void md_config_t::apply_changes(std::ostream *oss)
644 {
645 Mutex::Locker l(lock);
646 /*
647 * apply changes until the cluster name is assigned
648 */
649 if (cluster.size())
650 _apply_changes(oss);
651 }
652
653 bool md_config_t::_internal_field(const string& s)
654 {
655 if (s == "internal_safe_to_start_threads")
656 return true;
657 return false;
658 }
659
660 void md_config_t::_apply_changes(std::ostream *oss)
661 {
662 /* Maps observers to the configuration options that they care about which
663 * have changed. */
664 typedef std::map < md_config_obs_t*, std::set <std::string> > rev_obs_map_t;
665
666 expand_all_meta();
667
668 // create the reverse observer mapping, mapping observers to the set of
669 // changed keys that they'll get.
670 rev_obs_map_t robs;
671 std::set <std::string> empty_set;
672 char buf[128];
673 char *bufptr = (char*)buf;
674 for (changed_set_t::const_iterator c = changed.begin();
675 c != changed.end(); ++c) {
676 const std::string &key(*c);
677 pair < obs_map_t::iterator, obs_map_t::iterator >
678 range(observers.equal_range(key));
679 if ((oss) &&
680 (!_get_val(key.c_str(), &bufptr, sizeof(buf))) &&
681 !_internal_field(key)) {
682 (*oss) << key << " = '" << buf << "' ";
683 if (range.first == range.second) {
684 (*oss) << "(not observed, change may require restart) ";
685 }
686 }
687 for (obs_map_t::iterator r = range.first; r != range.second; ++r) {
688 rev_obs_map_t::value_type robs_val(r->second, empty_set);
689 pair < rev_obs_map_t::iterator, bool > robs_ret(robs.insert(robs_val));
690 std::set <std::string> &keys(robs_ret.first->second);
691 keys.insert(key);
692 }
693 }
694
695 changed.clear();
696
697 // Make any pending observer callbacks
698 for (rev_obs_map_t::const_iterator r = robs.begin(); r != robs.end(); ++r) {
699 md_config_obs_t *obs = r->first;
700 obs->handle_conf_change(this, r->second);
701 }
702
703 }
704
705 void md_config_t::call_all_observers()
706 {
707 std::map<md_config_obs_t*,std::set<std::string> > obs;
708 {
709 Mutex::Locker l(lock);
710
711 expand_all_meta();
712
713 for (obs_map_t::iterator r = observers.begin(); r != observers.end(); ++r) {
714 obs[r->second].insert(r->first);
715 }
716 }
717 for (std::map<md_config_obs_t*,std::set<std::string> >::iterator p = obs.begin();
718 p != obs.end();
719 ++p) {
720 p->first->handle_conf_change(this, p->second);
721 }
722 }
723
724 int md_config_t::injectargs(const std::string& s, std::ostream *oss)
725 {
726 int ret;
727 Mutex::Locker l(lock);
728 char b[s.length()+1];
729 strcpy(b, s.c_str());
730 std::vector<const char*> nargs;
731 char *p = b;
732 while (*p) {
733 nargs.push_back(p);
734 while (*p && *p != ' ') p++;
735 if (!*p)
736 break;
737 *p++ = 0;
738 while (*p && *p == ' ') p++;
739 }
740 ret = parse_injectargs(nargs, oss);
741 if (!nargs.empty()) {
742 *oss << " failed to parse arguments: ";
743 std::string prefix;
744 for (std::vector<const char*>::const_iterator i = nargs.begin();
745 i != nargs.end(); ++i) {
746 *oss << prefix << *i;
747 prefix = ",";
748 }
749 *oss << "\n";
750 ret = -EINVAL;
751 }
752 _apply_changes(oss);
753 return ret;
754 }
755
756 void md_config_t::set_val_or_die(const char *key, const char *val)
757 {
758 int ret = set_val(key, val);
759 assert(ret == 0);
760 }
761
762 struct is_integer_member : public boost::static_visitor<bool> {
763 template<typename T,
764 typename boost::enable_if<boost::is_integral<T>, int>::type = 0>
765 bool operator()(const T md_config_t::* /* member_ptr */) const {
766 return true;
767 }
768 template<typename T,
769 typename boost::enable_if_c<!boost::is_integral<T>::value, int>::type = 0>
770 bool operator()(const T md_config_t::* /* member_ptr */) const {
771 return false;
772 }
773 };
774
775 struct is_float_member : public boost::static_visitor<bool> {
776 template<typename T,
777 typename boost::enable_if<boost::is_float<T>, int>::type = 0>
778 bool operator()(const T md_config_t::* /* member_ptr */) const {
779 return true;
780 }
781 template<typename T,
782 typename boost::enable_if_c<!boost::is_float<T>::value, int>::type = 0>
783 bool operator()(const T md_config_t::* /* member_ptr */) const {
784 return false;
785 }
786 };
787
788 bool md_config_t::config_option::is_safe() const {
789 // for now integer and floating point options considered thread safe
790 return safe ||
791 boost::apply_visitor(is_integer_member(), md_member_ptr) ||
792 boost::apply_visitor(is_float_member(), md_member_ptr);
793 }
794
795 md_config_t::config_option const *md_config_t::find_config_option(const std::string &normalized_key) const
796 {
797 auto opt_it = std::find_if(config_options->begin(),
798 config_options->end(),
799 [normalized_key](const config_option &opt) -> bool {
800 return strcmp(normalized_key.c_str(), opt.name) == 0;
801 });
802 return config_options->end() == opt_it ? nullptr : &(*opt_it);
803 }
804
805 int md_config_t::set_val(const char *key, const char *val, bool meta)
806 {
807 Mutex::Locker l(lock);
808 if (!key)
809 return -EINVAL;
810 if (!val)
811 return -EINVAL;
812
813 std::string v(val);
814 if (meta)
815 expand_meta(v, &std::cerr);
816
817 string k(ConfFile::normalize_key_name(key));
818
819 // subsystems?
820 if (strncmp(k.c_str(), "debug_", 6) == 0) {
821 for (int o = 0; o < subsys.get_num(); o++) {
822 std::string as_option = "debug_" + subsys.get_name(o);
823 if (k == as_option) {
824 int log, gather;
825 int r = sscanf(v.c_str(), "%d/%d", &log, &gather);
826 if (r >= 1) {
827 if (r < 2)
828 gather = log;
829 // cout << "subsys " << subsys.get_name(o) << " log " << log << " gather " << gather << std::endl;
830 subsys.set_log_level(o, log);
831 subsys.set_gather_level(o, gather);
832 return 0;
833 }
834 return -EINVAL;
835 }
836 }
837 }
838
839 config_option const *opt = find_config_option(k);
840 if (opt) {
841 if ((!opt->is_safe()) && internal_safe_to_start_threads) {
842 // If threads have been started and the option is not thread safe
843 if (observers.find(opt->name) == observers.end()) {
844 // And there is no observer to safely change it...
845 // You lose.
846 return -ENOSYS;
847 }
848 }
849
850 std::string error_message;
851 int r = set_val_impl(v, opt, &error_message);
852 return r;
853 }
854
855 // couldn't find a configuration option with key 'key'
856 return -ENOENT;
857 }
858
859
860 int md_config_t::get_val(const char *key, char **buf, int len) const
861 {
862 Mutex::Locker l(lock);
863 return _get_val(key, buf,len);
864 }
865
866 md_config_t::config_value_t md_config_t::get_val_generic(const char *key) const
867 {
868 Mutex::Locker l(lock);
869 return _get_val(key);
870 }
871
872 class get_value_generic_visitor : public boost::static_visitor<md_config_t::config_value_t> {
873 md_config_t const *conf;
874 public:
875 explicit get_value_generic_visitor(md_config_t const *conf_) : conf(conf_) { }
876 template<typename T> md_config_t::config_value_t operator()(const T md_config_t::* member_ptr) {
877 return md_config_t::config_value_t(conf->*member_ptr);
878 }
879 };
880
881 md_config_t::config_value_t md_config_t::_get_val(const char *key) const
882 {
883 assert(lock.is_locked());
884
885 if (!key)
886 return config_value_t(invalid_config_value_t());
887
888 // In key names, leading and trailing whitespace are not significant.
889 string k(ConfFile::normalize_key_name(key));
890
891 config_option const *opt = find_config_option(k);
892 if (!opt) {
893 return config_value_t(invalid_config_value_t());
894 }
895 get_value_generic_visitor gvv(this);
896 return boost::apply_visitor(gvv, opt->md_member_ptr);
897 }
898
899 int md_config_t::_get_val(const char *key, std::string *value) const {
900 assert(lock.is_locked());
901
902 std::string normalized_key(ConfFile::normalize_key_name(key));
903 config_value_t config_value = _get_val(normalized_key.c_str());
904 if (!boost::get<invalid_config_value_t>(&config_value)) {
905 ostringstream oss;
906 if (bool *flag = boost::get<bool>(&config_value)) {
907 oss << (*flag ? "true" : "false");
908 } else {
909 oss << config_value;
910 }
911 *value = oss.str();
912 return 0;
913 }
914 return -ENOENT;
915 }
916
917 int md_config_t::_get_val(const char *key, char **buf, int len) const
918 {
919 assert(lock.is_locked());
920
921 if (!key)
922 return -EINVAL;
923
924 string k(ConfFile::normalize_key_name(key));
925
926 config_value_t cval = _get_val(k.c_str());
927 if (!boost::get<invalid_config_value_t>(&cval)) {
928 ostringstream oss;
929 if (bool *flagp = boost::get<bool>(&cval)) {
930 oss << (*flagp ? "true" : "false");
931 } else {
932 oss << cval;
933 }
934 string str(oss.str());
935 int l = strlen(str.c_str()) + 1;
936 if (len == -1) {
937 *buf = (char*)malloc(l);
938 if (!*buf)
939 return -ENOMEM;
940 strcpy(*buf, str.c_str());
941 return 0;
942 }
943 snprintf(*buf, len, "%s", str.c_str());
944 return (l > len) ? -ENAMETOOLONG : 0;
945 }
946
947 // subsys?
948 for (int o = 0; o < subsys.get_num(); o++) {
949 std::string as_option = "debug_" + subsys.get_name(o);
950 if (k == as_option) {
951 if (len == -1) {
952 *buf = (char*)malloc(20);
953 len = 20;
954 }
955 int l = snprintf(*buf, len, "%d/%d", subsys.get_log_level(o), subsys.get_gather_level(o));
956 return (l == len) ? -ENAMETOOLONG : 0;
957 }
958 }
959
960 // couldn't find a configuration option with key 'k'
961 return -ENOENT;
962 }
963
964 void md_config_t::get_all_keys(std::vector<std::string> *keys) const {
965 const std::string negative_flag_prefix("no_");
966
967 keys->clear();
968 keys->reserve(config_options->size());
969 for (auto& opt: *config_options) {
970 keys->push_back(opt.name);
971 if (opt.type == OPT_BOOL) {
972 keys->push_back(negative_flag_prefix + opt.name);
973 }
974 }
975 for (int i = 0; i < subsys.get_num(); ++i) {
976 keys->push_back("debug_" + subsys.get_name(i));
977 }
978 }
979
980 /* The order of the sections here is important. The first section in the
981 * vector is the "highest priority" section; if we find it there, we'll stop
982 * looking. The lowest priority section is the one we look in only if all
983 * others had nothing. This should always be the global section.
984 */
985 void md_config_t::get_my_sections(std::vector <std::string> &sections) const
986 {
987 Mutex::Locker l(lock);
988 _get_my_sections(sections);
989 }
990
991 void md_config_t::_get_my_sections(std::vector <std::string> &sections) const
992 {
993 assert(lock.is_locked());
994 sections.push_back(name.to_str());
995
996 sections.push_back(name.get_type_name());
997
998 sections.push_back("global");
999 }
1000
1001 // Return a list of all sections
1002 int md_config_t::get_all_sections(std::vector <std::string> &sections) const
1003 {
1004 Mutex::Locker l(lock);
1005 for (ConfFile::const_section_iter_t s = cf.sections_begin();
1006 s != cf.sections_end(); ++s) {
1007 sections.push_back(s->first);
1008 }
1009 return 0;
1010 }
1011
1012 int md_config_t::get_val_from_conf_file(const std::vector <std::string> &sections,
1013 const char *key, std::string &out, bool emeta) const
1014 {
1015 Mutex::Locker l(lock);
1016 return _get_val_from_conf_file(sections, key, out, emeta);
1017 }
1018
1019 int md_config_t::_get_val_from_conf_file(const std::vector <std::string> &sections,
1020 const char *key, std::string &out, bool emeta) const
1021 {
1022 assert(lock.is_locked());
1023 std::vector <std::string>::const_iterator s = sections.begin();
1024 std::vector <std::string>::const_iterator s_end = sections.end();
1025 for (; s != s_end; ++s) {
1026 int ret = cf.read(s->c_str(), key, out);
1027 if (ret == 0) {
1028 if (emeta)
1029 expand_meta(out, &std::cerr);
1030 return 0;
1031 }
1032 else if (ret != -ENOENT)
1033 return ret;
1034 }
1035 return -ENOENT;
1036 }
1037
1038 int md_config_t::set_val_impl(const std::string &val, config_option const *opt,
1039 std::string *error_message)
1040 {
1041 assert(lock.is_locked());
1042 std::string value(val);
1043 if (opt->validator) {
1044 int r = opt->validator(&value, error_message);
1045 if (r < 0) {
1046 return r;
1047 }
1048 }
1049
1050 int ret = set_val_raw(value.c_str(), opt);
1051 if (ret)
1052 return ret;
1053 changed.insert(opt->name);
1054 return 0;
1055 }
1056
1057 template<typename T> struct strtox_helper;
1058
1059 template<> struct strtox_helper<float> {
1060 static inline void apply(const char *val, float &x, std::string &err) {
1061 x = strict_strtof(val, &err);
1062 }
1063 };
1064
1065 template<> struct strtox_helper<double> {
1066 static inline void apply(const char *val, double &x, std::string &err) {
1067 x = strict_strtod(val, &err);
1068 }
1069 };
1070
1071 template<typename T> static inline int strict_strtox(const char *val, T &x) {
1072 std::string err;
1073 strtox_helper<T>::apply(val, x, err);
1074 return err.empty() ? 0 : -EINVAL;
1075 }
1076
1077 class set_value_visitor : public boost::static_visitor<int> {
1078 md_config_t const *conf;
1079 const char *val;
1080 public:
1081 explicit set_value_visitor(md_config_t const *conf_, const char *val_) :
1082 conf(conf_), val(val_) { }
1083
1084 int operator()(const std::string md_config_t::* member_ptr) {
1085 auto *ptr = const_cast<std::string *>(&(conf->*member_ptr));
1086 *ptr = val ? val : "";
1087 return 0;
1088 }
1089
1090 int operator()(const bool md_config_t::* member_ptr) {
1091 bool *ptr = const_cast<bool *>(&(conf->*member_ptr));
1092 if (strcasecmp(val, "false") == 0) {
1093 *ptr = false;
1094 } else if (strcasecmp(val, "true") == 0) {
1095 *ptr = true;
1096 } else {
1097 std::string err;
1098 int b = strict_strtol(val, 10, &err);
1099 if (!err.empty()) {
1100 return -EINVAL;
1101 }
1102 *ptr = !!b;
1103 }
1104 return 0;
1105 }
1106
1107 // type has parse() member function
1108 template<typename T,
1109 typename boost::enable_if<boost::is_member_function_pointer<decltype(&T::parse)>, int>::type = 0>
1110 int operator()(const T md_config_t::* member_ptr) {
1111 T *obj = const_cast<T *>(&(conf->*member_ptr));
1112 if (!obj->parse(val)) {
1113 return -EINVAL;
1114 }
1115 return 0;
1116 }
1117
1118 // float, double
1119 template<typename T,
1120 typename boost::enable_if<boost::is_floating_point<T>, int>::type = 0>
1121 int operator()(const T md_config_t::* member_ptr) {
1122 T* ptr = const_cast<T *>(&(conf->*member_ptr));
1123 return strict_strtox(val, *ptr);
1124 }
1125
1126 // integers
1127 template<typename T,
1128 typename boost::enable_if_c<boost::is_integral<T>::value &&
1129 !boost::is_same<T, bool>::value, int>::type = 0>
1130 int operator()(const T md_config_t::* member_ptr) {
1131 std::string err;
1132 T f = strict_si_cast<T>(val, &err);
1133 if (!err.empty()) {
1134 return -EINVAL;
1135 }
1136 T *ptr = const_cast<T *>(&(conf->*member_ptr));
1137 *ptr = f;
1138 return 0;
1139 }
1140 };
1141
1142 int md_config_t::set_val_raw(const char *val, config_option const *opt)
1143 {
1144 assert(lock.is_locked());
1145 set_value_visitor svv(this, val);
1146 return boost::apply_visitor(svv, opt->md_member_ptr);
1147 }
1148
1149 static const char *CONF_METAVARIABLES[] = {
1150 "data_dir", // put this first: it may contain some of the others
1151 "cluster", "type", "name", "host", "num", "id", "pid", "cctid"
1152 };
1153 static const int NUM_CONF_METAVARIABLES =
1154 (sizeof(CONF_METAVARIABLES) / sizeof(CONF_METAVARIABLES[0]));
1155
1156 void md_config_t::expand_all_meta()
1157 {
1158 // Expand all metavariables
1159 ostringstream oss;
1160 for (auto& opt: *config_options) {
1161 std::string *str;
1162 opt.conf_ptr(str, this);
1163 if (str) {
1164 list<config_option const *> stack;
1165 expand_meta(*str, &opt, stack, &oss);
1166 }
1167 }
1168 cerr << oss.str();
1169 }
1170
1171 bool md_config_t::expand_meta(std::string &origval,
1172 std::ostream *oss) const
1173 {
1174 list<config_option const *> stack;
1175 return expand_meta(origval, NULL, stack, oss);
1176 }
1177
1178 bool md_config_t::expand_meta(std::string &origval,
1179 config_option const *opt,
1180 std::list<config_option const *> stack,
1181 std::ostream *oss) const
1182 {
1183 assert(lock.is_locked());
1184
1185 // no $ means no variable expansion is necessary
1186 if (origval.find("$") == string::npos)
1187 return false;
1188
1189 // ignore an expansion loop and create a human readable
1190 // message about it
1191 if (opt) {
1192 for (list<config_option const *>::iterator i = stack.begin();
1193 i != stack.end();
1194 ++i) {
1195 if (strcmp(opt->name, (*i)->name) == 0) {
1196 *oss << "variable expansion loop at "
1197 << opt->name << "=" << origval << std::endl;
1198 *oss << "expansion stack: " << std::endl;
1199 for (list<config_option const *>::iterator j = stack.begin();
1200 j != stack.end();
1201 ++j) {
1202 *oss << (*j)->name << "=" << *((*j)->conf_ptr<std::string>(this)) << std::endl;
1203 }
1204 return false;
1205 }
1206 }
1207 }
1208
1209 if (opt)
1210 stack.push_front(opt);
1211
1212 bool found_meta = false;
1213 string out;
1214 string val = origval;
1215 for (string::size_type s = 0; s < val.size(); ) {
1216 if (val[s] != '$') {
1217 out += val[s++];
1218 continue;
1219 }
1220
1221 // try to parse the variable name into var, either \$\{(.+)\} or
1222 // \$[a-z\_]+
1223 const char *valid_chars = "abcdefghijklmnopqrstuvwxyz_";
1224 string var;
1225 size_t endpos = 0;
1226 if (val[s+1] == '{') {
1227 // ...${foo_bar}...
1228 endpos = val.find_first_not_of(valid_chars, s+2);
1229 if (endpos != std::string::npos &&
1230 val[endpos] == '}') {
1231 var = val.substr(s+2, endpos-s-2);
1232 endpos++;
1233 }
1234 } else {
1235 // ...$foo...
1236 endpos = val.find_first_not_of(valid_chars, s+1);
1237 if (endpos != std::string::npos)
1238 var = val.substr(s+1, endpos-s-1);
1239 else
1240 var = val.substr(s+1);
1241 }
1242
1243 bool expanded = false;
1244 if (var.length()) {
1245 // special metavariable?
1246 for (int i = 0; i < NUM_CONF_METAVARIABLES; ++i) {
1247 if (var != CONF_METAVARIABLES[i])
1248 continue;
1249 //cout << " meta match of " << var << " " << CONF_METAVARIABLES[i] << std::endl;
1250 if (var == "type")
1251 out += name.get_type_name();
1252 else if (var == "cluster")
1253 out += cluster;
1254 else if (var == "name")
1255 out += name.to_cstr();
1256 else if (var == "host")
1257 {
1258 if (host == "")
1259 out += ceph_get_short_hostname();
1260 else
1261 out += host;
1262 }
1263 else if (var == "num")
1264 out += name.get_id().c_str();
1265 else if (var == "id")
1266 out += name.get_id().c_str();
1267 else if (var == "pid")
1268 out += stringify(getpid());
1269 else if (var == "cctid")
1270 out += stringify((unsigned long long)this);
1271 else if (var == "data_dir") {
1272 if (data_dir_option.length()) {
1273 char *vv = NULL;
1274 _get_val(data_dir_option.c_str(), &vv, -1);
1275 string tmp = vv;
1276 free(vv);
1277 expand_meta(tmp, NULL, stack, oss);
1278 out += tmp;
1279 } else {
1280 // this isn't really right, but it'll result in a mangled
1281 // non-existent path that will fail any search list
1282 out += "$data_dir";
1283 }
1284 } else
1285 ceph_abort(); // unreachable
1286 expanded = true;
1287 }
1288
1289 if (!expanded) {
1290 // config option?
1291 for (auto& opt: *config_options) {
1292 if (var == opt.name) {
1293 string *origval;
1294 opt.conf_ptr(origval, const_cast<md_config_t *>(this));
1295 if (origval) {
1296 expand_meta(*origval, &opt, stack, oss);
1297 out += *origval;
1298 } else {
1299 char *vv = NULL;
1300 _get_val(opt.name, &vv, -1);
1301 out += vv;
1302 free(vv);
1303 }
1304 expanded = true;
1305 break;
1306 }
1307 }
1308 }
1309 }
1310
1311 if (expanded) {
1312 found_meta = true;
1313 s = endpos;
1314 } else {
1315 out += val[s++];
1316 }
1317 }
1318 // override the original value with the expanded value
1319 origval = out;
1320 return found_meta;
1321 }
1322
1323 void md_config_t::diff(
1324 const md_config_t *other,
1325 map<string, pair<string, string> > *diff,
1326 set<string> *unknown)
1327 {
1328 diff_helper(other, diff, unknown);
1329 }
1330 void md_config_t::diff(
1331 const md_config_t *other,
1332 map<string, pair<string, string> > *diff,
1333 set<string> *unknown, const string& setting)
1334 {
1335 diff_helper(other, diff, unknown, setting);
1336 }
1337
1338 void md_config_t::diff_helper(
1339 const md_config_t *other,
1340 map<string,pair<string,string> > *diff,
1341 set<string> *unknown, const string& setting)
1342 {
1343 Mutex::Locker l(lock);
1344
1345 char local_buf[4096];
1346 char other_buf[4096];
1347 for (auto& opt : *config_options) {
1348 if (!setting.empty()) {
1349 if (setting != opt.name) {
1350 continue;
1351 }
1352 }
1353 memset(local_buf, 0, sizeof(local_buf));
1354 memset(other_buf, 0, sizeof(other_buf));
1355
1356 char *other_val = other_buf;
1357 int err = other->get_val(opt.name, &other_val, sizeof(other_buf));
1358 if (err < 0) {
1359 if (err == -ENOENT) {
1360 unknown->insert(opt.name);
1361 }
1362 continue;
1363 }
1364
1365 char *local_val = local_buf;
1366 err = _get_val(opt.name, &local_val, sizeof(local_buf));
1367 if (err != 0)
1368 continue;
1369
1370 if (strcmp(local_val, other_val))
1371 diff->insert(make_pair(opt.name, make_pair(local_val, other_val)));
1372 else if (!setting.empty()) {
1373 diff->insert(make_pair(opt.name, make_pair(local_val, other_val)));
1374 break;
1375 }
1376 }
1377 }
1378
1379 void md_config_t::complain_about_parse_errors(CephContext *cct)
1380 {
1381 ::complain_about_parse_errors(cct, &parse_errors);
1382 }
1383
1384 void md_config_t::validate_default_settings() {
1385 Mutex::Locker l(lock);
1386 for (auto &opt : *config_options) {
1387 // normalize config defaults using their validator
1388 if (opt.validator) {
1389 std::string value;
1390 int r = _get_val(opt.name, &value);
1391 assert(r == 0);
1392
1393 std::string error_message;
1394 r = set_val_impl(value.c_str(), &opt, &error_message);
1395 assert(r == 0);
1396 }
1397 }
1398 }