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