]> git.proxmox.com Git - ceph.git/blame - ceph/src/common/config.cc
import 14.2.4 nautilus point release
[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
11fdf7f2
TL
15#include <boost/type_traits.hpp>
16
7c673cae
FG
17#include "common/ceph_argparse.h"
18#include "common/common_init.h"
19#include "common/config.h"
11fdf7f2 20#include "common/config_obs.h"
7c673cae 21#include "include/str_list.h"
7c673cae 22#include "include/stringify.h"
7c673cae
FG
23#include "osd/osd_types.h"
24#include "common/errno.h"
25#include "common/hostname.h"
11fdf7f2 26#include "common/dout.h"
7c673cae
FG
27
28/* Don't use standard Ceph logging in this file.
29 * We can't use logging until it's initialized, and a lot of the necessary
30 * initialization happens here.
31 */
32#undef dout
7c673cae
FG
33#undef pdout
34#undef derr
7c673cae 35#undef generic_dout
11fdf7f2
TL
36
37// set set_mon_vals()
38#define dout_subsys ceph_subsys_monc
7c673cae
FG
39
40using std::map;
41using std::list;
7c673cae
FG
42using std::ostringstream;
43using std::pair;
7c673cae
FG
44using std::string;
45
11fdf7f2 46static const char *CEPH_CONF_FILE_DEFAULT = "$data_dir/config, /etc/ceph/$cluster.conf, $home/.ceph/$cluster.conf, $cluster.conf"
7c673cae
FG
47#if defined(__FreeBSD__)
48 ", /usr/local/etc/ceph/$cluster.conf"
49#endif
50 ;
51
52#define _STR(x) #x
53#define STRINGIFY(x) _STR(x)
54
11fdf7f2
TL
55const char *ceph_conf_level_name(int level)
56{
57 switch (level) {
58 case CONF_DEFAULT: return "default"; // built-in default
59 case CONF_MON: return "mon"; // monitor config database
60 case CONF_ENV: return "env"; // process environment (CEPH_ARGS)
61 case CONF_FILE: return "file"; // ceph.conf file
62 case CONF_CMDLINE: return "cmdline"; // process command line args
63 case CONF_OVERRIDE: return "override"; // injectargs or 'config set' at runtime
64 case CONF_FINAL: return "final";
65 default: return "???";
66 }
67}
68
7c673cae
FG
69int ceph_resolve_file_search(const std::string& filename_list,
70 std::string& result)
71{
72 list<string> ls;
73 get_str_list(filename_list, ls);
74
75 int ret = -ENOENT;
76 list<string>::iterator iter;
77 for (iter = ls.begin(); iter != ls.end(); ++iter) {
91327a77 78 int fd = ::open(iter->c_str(), O_RDONLY|O_CLOEXEC);
7c673cae
FG
79 if (fd < 0) {
80 ret = -errno;
81 continue;
82 }
83 close(fd);
84 result = *iter;
85 return 0;
86 }
87
88 return ret;
89}
90
11fdf7f2 91static int conf_stringify(const Option::value_t& v, string *out)
c07f9fc5 92{
11fdf7f2
TL
93 if (boost::get<boost::blank>(&v)) {
94 return -ENOENT;
95 }
96 *out = Option::to_str(v);
97 return 0;
98}
c07f9fc5 99
11fdf7f2
TL
100md_config_t::md_config_t(ConfigValues& values,
101 const ConfigTracker& tracker,
102 bool is_daemon)
103 : is_daemon(is_daemon)
104{
c07f9fc5
FG
105 // Load the compile-time list of Option into
106 // a map so that we can resolve keys quickly.
107 for (const auto &i : ceph_options) {
108 if (schema.count(i.name)) {
109 // We may be instantiated pre-logging so send
110 std::cerr << "Duplicate config key in schema: '" << i.name << "'"
111 << std::endl;
11fdf7f2 112 ceph_abort();
c07f9fc5 113 }
11fdf7f2
TL
114 schema.emplace(std::piecewise_construct,
115 std::forward_as_tuple(i.name),
116 std::forward_as_tuple(i));
117 }
118
119 // Define the debug_* options as well.
120 subsys_options.reserve(values.subsys.get_num());
121 for (unsigned i = 0; i < values.subsys.get_num(); ++i) {
122 string name = string("debug_") + values.subsys.get_name(i);
123 subsys_options.push_back(
124 Option(name, Option::TYPE_STR, Option::LEVEL_ADVANCED));
125 Option& opt = subsys_options.back();
126 opt.set_default(stringify(values.subsys.get_log_level(i)) + "/" +
127 stringify(values.subsys.get_gather_level(i)));
128 string desc = string("Debug level for ") + values.subsys.get_name(i);
129 opt.set_description(desc.c_str());
130 opt.set_flag(Option::FLAG_RUNTIME);
131 opt.set_long_description("The value takes the form 'N' or 'N/M' where N and M are values between 0 and 99. N is the debug level to log (all values below this are included), and M is the level to gather and buffer in memory. In the event of a crash, the most recent items <= M are dumped to the log file.");
132 opt.set_subsys(i);
133 opt.set_validator([](std::string *value, std::string *error_message) {
134 int m, n;
135 int r = sscanf(value->c_str(), "%d/%d", &m, &n);
136 if (r >= 1) {
137 if (m < 0 || m > 99) {
138 *error_message = "value must be in range [0, 99]";
139 return -ERANGE;
140 }
141 if (r == 2) {
142 if (n < 0 || n > 99) {
143 *error_message = "value must be in range [0, 99]";
144 return -ERANGE;
145 }
146 } else {
147 // normalize to M/N
148 n = m;
149 *value = stringify(m) + "/" + stringify(n);
150 }
151 } else {
152 *error_message = "value must take the form N or N/M, where N and M are integers";
153 return -EINVAL;
154 }
155 return 0;
156 });
157 }
158 for (auto& opt : subsys_options) {
159 schema.emplace(opt.name, opt);
c07f9fc5
FG
160 }
161
162 // Populate list of legacy_values according to the OPTION() definitions
163 // Note that this is just setting up our map of name->member ptr. The
164 // default values etc will get loaded in along with new-style data,
165 // as all loads write to both the values map, and the legacy
166 // members if present.
167 legacy_values = {
168#define OPTION(name, type) \
11fdf7f2 169 {std::string(STRINGIFY(name)), &ConfigValues::name},
c07f9fc5
FG
170#define SAFE_OPTION(name, type) OPTION(name, type)
171#include "common/legacy_config_opts.h"
7c673cae 172#undef OPTION
7c673cae 173#undef SAFE_OPTION
c07f9fc5 174 };
7c673cae 175
c07f9fc5 176 validate_schema();
7c673cae 177
11fdf7f2 178 // Validate default values from the schema
c07f9fc5
FG
179 for (const auto &i : schema) {
180 const Option &opt = i.second;
c07f9fc5 181 if (opt.type == Option::TYPE_STR) {
11fdf7f2
TL
182 bool has_daemon_default = !boost::get<boost::blank>(&opt.daemon_value);
183 Option::value_t default_val;
184 if (is_daemon && has_daemon_default) {
185 default_val = opt.daemon_value;
186 } else {
187 default_val = opt.value;
188 }
c07f9fc5
FG
189 // We call pre_validate as a sanity check, but also to get any
190 // side effect (value modification) from the validator.
191 std::string *def_str = boost::get<std::string>(&default_val);
11fdf7f2 192 std::string val = *def_str;
c07f9fc5 193 std::string err;
11fdf7f2 194 if (opt.pre_validate(&val, &err) != 0) {
c07f9fc5
FG
195 std::cerr << "Default value " << opt.name << "=" << *def_str << " is "
196 "invalid: " << err << std::endl;
197
198 // This is the compiled-in default that is failing its own option's
199 // validation, so this is super-invalid and should never make it
200 // past a pull request: crash out.
11fdf7f2
TL
201 ceph_abort();
202 }
203 if (val != *def_str) {
204 // if the validator normalizes the string into a different form than
205 // what was compiled in, use that.
206 set_val_default(values, tracker, opt.name, val);
c07f9fc5
FG
207 }
208 }
c07f9fc5 209 }
7c673cae 210
c07f9fc5 211 // Copy out values (defaults) into any legacy (C struct member) fields
11fdf7f2
TL
212 update_legacy_vals(values);
213}
7c673cae 214
11fdf7f2
TL
215md_config_t::~md_config_t()
216{
c07f9fc5
FG
217}
218
219/**
220 * Sanity check schema. Assert out on failures, to ensure any bad changes
221 * cannot possibly pass any testing and make it into a release.
222 */
223void md_config_t::validate_schema()
7c673cae 224{
c07f9fc5
FG
225 for (const auto &i : schema) {
226 const auto &opt = i.second;
227 for (const auto &see_also_key : opt.see_also) {
228 if (schema.count(see_also_key) == 0) {
229 std::cerr << "Non-existent see-also key '" << see_also_key
230 << "' on option '" << opt.name << "'" << std::endl;
11fdf7f2 231 ceph_abort();
c07f9fc5
FG
232 }
233 }
234 }
7c673cae 235
c07f9fc5
FG
236 for (const auto &i : legacy_values) {
237 if (schema.count(i.first) == 0) {
238 std::cerr << "Schema is missing legacy field '" << i.first << "'"
239 << std::endl;
11fdf7f2 240 ceph_abort();
c07f9fc5
FG
241 }
242 }
7c673cae
FG
243}
244
11fdf7f2 245const Option *md_config_t::find_option(const string& name) const
7c673cae 246{
11fdf7f2
TL
247 auto p = schema.find(name);
248 if (p != schema.end()) {
249 return &p->second;
250 }
251 return nullptr;
7c673cae
FG
252}
253
11fdf7f2
TL
254void md_config_t::set_val_default(ConfigValues& values,
255 const ConfigTracker& tracker,
256 const string& name, const std::string& val)
7c673cae 257{
11fdf7f2
TL
258 const Option *o = find_option(name);
259 ceph_assert(o);
260 string err;
261 int r = _set_val(values, tracker, val, *o, CONF_DEFAULT, &err);
262 ceph_assert(r >= 0);
7c673cae
FG
263}
264
11fdf7f2
TL
265int md_config_t::set_mon_vals(CephContext *cct,
266 ConfigValues& values,
267 const ConfigTracker& tracker,
268 const map<string,string>& kv,
269 config_callback config_cb)
7c673cae 270{
11fdf7f2 271 ignored_mon_values.clear();
f64942e4 272
11fdf7f2
TL
273 if (!config_cb) {
274 ldout(cct, 4) << __func__ << " no callback set" << dendl;
275 }
f64942e4 276
11fdf7f2
TL
277 for (auto& i : kv) {
278 if (config_cb) {
279 if (config_cb(i.first, i.second)) {
280 ldout(cct, 4) << __func__ << " callback consumed " << i.first << dendl;
281 continue;
282 }
283 ldout(cct, 4) << __func__ << " callback ignored " << i.first << dendl;
7c673cae 284 }
11fdf7f2
TL
285 const Option *o = find_option(i.first);
286 if (!o) {
287 ldout(cct,10) << __func__ << " " << i.first << " = " << i.second
288 << " (unrecognized option)" << dendl;
289 continue;
290 }
291 if (o->has_flag(Option::FLAG_NO_MON_UPDATE)) {
292 ignored_mon_values.emplace(i);
293 continue;
294 }
295 std::string err;
296 int r = _set_val(values, tracker, i.second, *o, CONF_MON, &err);
297 if (r < 0) {
298 lderr(cct) << __func__ << " failed to set " << i.first << " = "
299 << i.second << ": " << err << dendl;
300 ignored_mon_values.emplace(i);
301 } else if (r == ConfigValues::SET_NO_CHANGE ||
302 r == ConfigValues::SET_NO_EFFECT) {
303 ldout(cct,20) << __func__ << " " << i.first << " = " << i.second
304 << " (no change)" << dendl;
305 } else if (r == ConfigValues::SET_HAVE_EFFECT) {
306 ldout(cct,10) << __func__ << " " << i.first << " = " << i.second << dendl;
307 } else {
308 ceph_abort();
7c673cae
FG
309 }
310 }
11fdf7f2
TL
311 values.for_each([&] (auto name, auto configs) {
312 auto config = configs.find(CONF_MON);
313 if (config == configs.end()) {
314 return;
315 }
316 if (kv.find(name) != kv.end()) {
317 return;
318 }
319 ldout(cct,10) << __func__ << " " << name
320 << " cleared (was " << Option::to_str(config->second) << ")"
321 << dendl;
322 values.rm_val(name, CONF_MON);
323 });
324 values_bl.clear();
325 update_legacy_vals(values);
326 return 0;
7c673cae
FG
327}
328
11fdf7f2
TL
329int md_config_t::parse_config_files(ConfigValues& values,
330 const ConfigTracker& tracker,
331 const char *conf_files_str,
7c673cae
FG
332 std::ostream *warnings,
333 int flags)
334{
7c673cae 335
11fdf7f2 336 if (safe_to_start_threads)
7c673cae
FG
337 return -ENOSYS;
338
11fdf7f2 339 if (!values.cluster.size() && !conf_files_str) {
7c673cae
FG
340 /*
341 * set the cluster name to 'ceph' when neither cluster name nor
342 * configuration file are specified.
343 */
11fdf7f2 344 values.cluster = "ceph";
7c673cae
FG
345 }
346
11fdf7f2 347 if (!conf_files_str) {
7c673cae
FG
348 const char *c = getenv("CEPH_CONF");
349 if (c) {
11fdf7f2 350 conf_files_str = c;
7c673cae
FG
351 }
352 else {
353 if (flags & CINIT_FLAG_NO_DEFAULT_CONFIG_FILE)
354 return 0;
11fdf7f2 355 conf_files_str = CEPH_CONF_FILE_DEFAULT;
7c673cae
FG
356 }
357 }
358
11fdf7f2
TL
359 std::list<std::string> conf_files;
360 get_str_list(conf_files_str, conf_files);
361 auto p = conf_files.begin();
362 while (p != conf_files.end()) {
7c673cae 363 string &s = *p;
11fdf7f2
TL
364 if (s.find("$data_dir") != string::npos &&
365 data_dir_option.empty()) {
366 // useless $data_dir item, skip
367 p = conf_files.erase(p);
7c673cae 368 } else {
11fdf7f2 369 early_expand_meta(values, s, warnings);
7c673cae
FG
370 ++p;
371 }
372 }
7c673cae
FG
373
374 // open new conf
375 list<string>::const_iterator c;
376 for (c = conf_files.begin(); c != conf_files.end(); ++c) {
377 cf.clear();
378 string fn = *c;
11fdf7f2 379
7c673cae
FG
380 int ret = cf.parse_file(fn.c_str(), &parse_errors, warnings);
381 if (ret == 0)
382 break;
383 else if (ret != -ENOENT)
384 return ret;
385 }
386 // it must have been all ENOENTs, that's the only way we got here
387 if (c == conf_files.end())
388 return -ENOENT;
389
11fdf7f2 390 if (values.cluster.size() == 0) {
7c673cae
FG
391 /*
392 * If cluster name is not set yet, use the prefix of the
393 * basename of configuration file as cluster name.
394 */
395 auto start = c->rfind('/') + 1;
396 auto end = c->find(".conf", start);
397 if (end == c->npos) {
398 /*
399 * If the configuration file does not follow $cluster.conf
400 * convention, we do the last try and assign the cluster to
401 * 'ceph'.
402 */
11fdf7f2 403 values.cluster = "ceph";
7c673cae 404 } else {
11fdf7f2 405 values.cluster = c->substr(start, end - start);
7c673cae
FG
406 }
407 }
408
409 std::vector <std::string> my_sections;
11fdf7f2 410 _get_my_sections(values, my_sections);
c07f9fc5
FG
411 for (const auto &i : schema) {
412 const auto &opt = i.second;
7c673cae 413 std::string val;
11fdf7f2 414 int ret = _get_val_from_conf_file(my_sections, opt.name, val);
7c673cae
FG
415 if (ret == 0) {
416 std::string error_message;
11fdf7f2
TL
417 int r = _set_val(values, tracker, val, opt, CONF_FILE, &error_message);
418 if (warnings != nullptr && (r < 0 || !error_message.empty())) {
7c673cae
FG
419 *warnings << "parse error setting '" << opt.name << "' to '" << val
420 << "'";
421 if (!error_message.empty()) {
422 *warnings << " (" << error_message << ")";
423 }
424 *warnings << std::endl;
425 }
426 }
427 }
428
7c673cae
FG
429 // Warn about section names that look like old-style section names
430 std::deque < std::string > old_style_section_names;
431 for (ConfFile::const_section_iter_t s = cf.sections_begin();
432 s != cf.sections_end(); ++s) {
433 const string &str(s->first);
434 if (((str.find("mds") == 0) || (str.find("mon") == 0) ||
435 (str.find("osd") == 0)) && (str.size() > 3) && (str[3] != '.')) {
436 old_style_section_names.push_back(str);
437 }
438 }
439 if (!old_style_section_names.empty()) {
440 ostringstream oss;
441 cerr << "ERROR! old-style section name(s) found: ";
442 string sep;
443 for (std::deque < std::string >::const_iterator os = old_style_section_names.begin();
444 os != old_style_section_names.end(); ++os) {
445 cerr << sep << *os;
446 sep = ", ";
447 }
448 cerr << ". Please use the new style section names that include a period.";
449 }
11fdf7f2
TL
450
451 update_legacy_vals(values);
452
7c673cae
FG
453 return 0;
454}
455
11fdf7f2
TL
456void md_config_t::parse_env(unsigned entity_type,
457 ConfigValues& values,
458 const ConfigTracker& tracker,
459 const char *args_var)
7c673cae 460{
11fdf7f2 461 if (safe_to_start_threads)
7c673cae 462 return;
11fdf7f2
TL
463 if (!args_var) {
464 args_var = "CEPH_ARGS";
465 }
81eedcae
TL
466 if (auto s = getenv("CEPH_KEYRING"); s) {
467 string err;
468 _set_val(values, tracker, s, *find_option("keyring"), CONF_ENV, &err);
11fdf7f2 469 }
81eedcae 470 if (auto dir = getenv("CEPH_LIB"); dir) {
11fdf7f2
TL
471 for (auto name : { "erasure_code_dir", "plugin_dir", "osd_class_dir" }) {
472 std::string err;
473 const Option *o = find_option(name);
474 ceph_assert(o);
475 _set_val(values, tracker, dir, *o, CONF_ENV, &err);
476 }
477 }
494da23a
TL
478
479 // Apply pod memory limits:
480 //
481 // There are two types of resource requests: `limits` and `requests`.
482 //
483 // - Requests: Used by the K8s scheduler to determine on which nodes to
484 // schedule the pods. This helps spread the pods to different nodes. This
485 // value should be conservative in order to make sure all the pods are
486 // schedulable. This corresponds to POD_MEMORY_REQUEST (set by the Rook
487 // CRD) and is the target memory utilization we try to maintain for daemons
488 // that respect it.
489 //
490 // If POD_MEMORY_REQUEST is present, we use it as the target.
491 //
492 // - Limits: At runtime, the container runtime (and Linux) will use the
493 // limits to see if the pod is using too many resources. In that case, the
494 // pod will be killed/restarted automatically if the pod goes over the limit.
495 // This should be higher than what is specified for requests (potentially
496 // much higher). This corresponds to the cgroup memory limit that will
497 // trigger the Linux OOM killer.
498 //
499 // If POD_MEMORY_LIMIT is present, we use it as the /default/ value for
500 // the target, which means it will only apply if the *_memory_target option
501 // isn't set via some other path (e.g., POD_MEMORY_REQUEST, or the cluster
502 // config, or whatever.)
503 //
504 // Here are the documented best practices:
505 // https://kubernetes.io/docs/tasks/configure-pod-container/assign-cpu-resource/#motivation-for-cpu-requests-and-limits
506 //
507 // When the operator creates the CephCluster CR, it will need to generate the
508 // desired requests and limits. As long as we are conservative in our choice
509 // for requests and generous with the limits we should be in a good place to
510 // get started.
511 //
512 // The support in Rook is already there for applying the limits as seen in
513 // these links.
514 //
515 // Rook docs on the resource requests and limits:
516 // https://rook.io/docs/rook/v1.0/ceph-cluster-crd.html#cluster-wide-resources-configuration-settings
517 // Example CR settings:
518 // https://github.com/rook/rook/blob/6d2ef936698593036185aabcb00d1d74f9c7bfc1/cluster/examples/kubernetes/ceph/cluster.yaml#L90
519 //
520 uint64_t pod_limit = 0, pod_request = 0;
521 if (auto pod_lim = getenv("POD_MEMORY_LIMIT"); pod_lim) {
81eedcae 522 string err;
494da23a 523 uint64_t v = atoll(pod_lim);
11fdf7f2
TL
524 if (v) {
525 switch (entity_type) {
526 case CEPH_ENTITY_TYPE_OSD:
494da23a
TL
527 {
528 double cgroup_ratio = get_val<double>(
529 values, "osd_memory_target_cgroup_limit_ratio");
530 if (cgroup_ratio > 0.0) {
531 pod_limit = v * cgroup_ratio;
532 // set osd_memory_target *default* based on cgroup limit, so that
533 // it can be overridden by any explicit settings elsewhere.
534 set_val_default(values, tracker,
535 "osd_memory_target", stringify(pod_limit));
536 }
537 }
11fdf7f2
TL
538 }
539 }
540 }
494da23a
TL
541 if (auto pod_req = getenv("POD_MEMORY_REQUEST"); pod_req) {
542 if (uint64_t v = atoll(pod_req); v) {
543 pod_request = v;
544 }
545 }
546 if (pod_request && pod_limit) {
547 // If both LIMIT and REQUEST are set, ensure that we use the
548 // min of request and limit*ratio. This is important
549 // because k8s set set LIMIT == REQUEST if only LIMIT is
550 // specified, and we want to apply the ratio in that case,
551 // even though REQUEST is present.
552 pod_request = std::min<uint64_t>(pod_request, pod_limit);
553 }
554 if (pod_request) {
555 string err;
556 switch (entity_type) {
557 case CEPH_ENTITY_TYPE_OSD:
558 _set_val(values, tracker, stringify(pod_request),
559 *find_option("osd_memory_target"),
560 CONF_ENV, &err);
561 break;
562 }
563 }
564
11fdf7f2
TL
565 if (getenv(args_var)) {
566 vector<const char *> env_args;
567 env_to_vec(env_args, args_var);
568 parse_argv(values, tracker, env_args, CONF_ENV);
7c673cae
FG
569 }
570}
571
11fdf7f2
TL
572void md_config_t::show_config(const ConfigValues& values,
573 std::ostream& out) const
7c673cae 574{
11fdf7f2 575 _show_config(values, &out, nullptr);
7c673cae
FG
576}
577
11fdf7f2
TL
578void md_config_t::show_config(const ConfigValues& values,
579 Formatter *f) const
7c673cae 580{
11fdf7f2 581 _show_config(values, nullptr, f);
7c673cae
FG
582}
583
11fdf7f2 584void md_config_t::config_options(Formatter *f) const
1adf2230 585{
1adf2230
AA
586 f->open_array_section("options");
587 for (const auto& i: schema) {
11fdf7f2 588 f->dump_object("option", i.second);
1adf2230
AA
589 }
590 f->close_section();
591}
592
11fdf7f2
TL
593void md_config_t::_show_config(const ConfigValues& values,
594 std::ostream *out, Formatter *f) const
7c673cae
FG
595{
596 if (out) {
11fdf7f2
TL
597 *out << "name = " << values.name << std::endl;
598 *out << "cluster = " << values.cluster << std::endl;
7c673cae
FG
599 }
600 if (f) {
11fdf7f2
TL
601 f->dump_string("name", stringify(values.name));
602 f->dump_string("cluster", values.cluster);
7c673cae 603 }
c07f9fc5
FG
604 for (const auto& i: schema) {
605 const Option &opt = i.second;
11fdf7f2
TL
606 string val;
607 conf_stringify(_get_val(values, opt), &val);
608 if (out) {
609 *out << opt.name << " = " << val << std::endl;
610 }
611 if (f) {
612 f->dump_string(opt.name.c_str(), val);
613 }
7c673cae
FG
614 }
615}
616
11fdf7f2
TL
617int md_config_t::parse_argv(ConfigValues& values,
618 const ConfigTracker& tracker,
619 std::vector<const char*>& args, int level)
7c673cae 620{
11fdf7f2 621 if (safe_to_start_threads) {
7c673cae
FG
622 return -ENOSYS;
623 }
624
7c673cae
FG
625 // In this function, don't change any parts of the configuration directly.
626 // Instead, use set_val to set them. This will allow us to send the proper
627 // observer notifications later.
628 std::string val;
629 for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
630 if (strcmp(*i, "--") == 0) {
631 /* Normally we would use ceph_argparse_double_dash. However, in this
632 * function we *don't* want to remove the double dash, because later
633 * argument parses will still need to see it. */
634 break;
635 }
636 else if (ceph_argparse_flag(args, i, "--show_conf", (char*)NULL)) {
637 cerr << cf << std::endl;
638 _exit(0);
639 }
640 else if (ceph_argparse_flag(args, i, "--show_config", (char*)NULL)) {
11fdf7f2 641 do_show_config = true;
7c673cae
FG
642 }
643 else if (ceph_argparse_witharg(args, i, &val, "--show_config_value", (char*)NULL)) {
11fdf7f2
TL
644 do_show_config_value = val;
645 }
646 else if (ceph_argparse_flag(args, i, "--no-mon-config", (char*)NULL)) {
647 values.no_mon_config = true;
648 }
649 else if (ceph_argparse_flag(args, i, "--log-early", (char*)NULL)) {
650 values.log_early = true;
651 }
652 else if (ceph_argparse_flag(args, i, "--mon-config", (char*)NULL)) {
653 values.no_mon_config = false;
7c673cae
FG
654 }
655 else if (ceph_argparse_flag(args, i, "--foreground", "-f", (char*)NULL)) {
11fdf7f2 656 set_val_or_die(values, tracker, "daemonize", "false");
7c673cae
FG
657 }
658 else if (ceph_argparse_flag(args, i, "-d", (char*)NULL)) {
11fdf7f2
TL
659 set_val_or_die(values, tracker, "daemonize", "false");
660 set_val_or_die(values, tracker, "log_file", "");
661 set_val_or_die(values, tracker, "log_to_stderr", "true");
662 set_val_or_die(values, tracker, "err_to_stderr", "true");
663 set_val_or_die(values, tracker, "log_to_syslog", "false");
7c673cae
FG
664 }
665 // Some stuff that we wanted to give universal single-character options for
666 // Careful: you can burn through the alphabet pretty quickly by adding
667 // to this list.
668 else if (ceph_argparse_witharg(args, i, &val, "--monmap", "-M", (char*)NULL)) {
11fdf7f2 669 set_val_or_die(values, tracker, "monmap", val.c_str());
7c673cae
FG
670 }
671 else if (ceph_argparse_witharg(args, i, &val, "--mon_host", "-m", (char*)NULL)) {
11fdf7f2 672 set_val_or_die(values, tracker, "mon_host", val.c_str());
7c673cae
FG
673 }
674 else if (ceph_argparse_witharg(args, i, &val, "--bind", (char*)NULL)) {
11fdf7f2 675 set_val_or_die(values, tracker, "public_addr", val.c_str());
7c673cae
FG
676 }
677 else if (ceph_argparse_witharg(args, i, &val, "--keyfile", "-K", (char*)NULL)) {
11fdf7f2
TL
678 bufferlist bl;
679 string err;
680 int r;
681 if (val == "-") {
682 r = bl.read_fd(STDIN_FILENO, 1024);
683 } else {
684 r = bl.read_file(val.c_str(), &err);
685 }
686 if (r >= 0) {
687 string k(bl.c_str(), bl.length());
688 set_val_or_die(values, tracker, "key", k.c_str());
689 }
7c673cae
FG
690 }
691 else if (ceph_argparse_witharg(args, i, &val, "--keyring", "-k", (char*)NULL)) {
11fdf7f2 692 set_val_or_die(values, tracker, "keyring", val.c_str());
7c673cae
FG
693 }
694 else if (ceph_argparse_witharg(args, i, &val, "--client_mountpoint", "-r", (char*)NULL)) {
11fdf7f2 695 set_val_or_die(values, tracker, "client_mountpoint", val.c_str());
7c673cae
FG
696 }
697 else {
11fdf7f2 698 int r = parse_option(values, tracker, args, i, NULL, level);
3efd9988
FG
699 if (r < 0) {
700 return r;
701 }
7c673cae
FG
702 }
703 }
11fdf7f2
TL
704 // meta expands could have modified anything. Copy it all out again.
705 update_legacy_vals(values);
706 return 0;
707}
708
709void md_config_t::do_argv_commands(const ConfigValues& values) const
710{
7c673cae 711
11fdf7f2
TL
712 if (do_show_config) {
713 _show_config(values, &cout, NULL);
7c673cae
FG
714 _exit(0);
715 }
716
11fdf7f2
TL
717 if (do_show_config_value.size()) {
718 string val;
719 int r = conf_stringify(_get_val(values, do_show_config_value, 0, &cerr),
720 &val);
7c673cae
FG
721 if (r < 0) {
722 if (r == -ENOENT)
11fdf7f2
TL
723 std::cerr << "failed to get config option '"
724 << do_show_config_value << "': option not found" << std::endl;
7c673cae 725 else
11fdf7f2
TL
726 std::cerr << "failed to get config option '"
727 << do_show_config_value << "': " << cpp_strerror(r)
728 << std::endl;
7c673cae
FG
729 _exit(1);
730 }
11fdf7f2 731 std::cout << val << std::endl;
7c673cae
FG
732 _exit(0);
733 }
7c673cae
FG
734}
735
11fdf7f2
TL
736int md_config_t::parse_option(ConfigValues& values,
737 const ConfigTracker& tracker,
738 std::vector<const char*>& args,
739 std::vector<const char*>::iterator& i,
740 ostream *oss,
741 int level)
7c673cae
FG
742{
743 int ret = 0;
c07f9fc5 744 size_t o = 0;
7c673cae
FG
745 std::string val;
746
c07f9fc5 747 std::string option_name;
7c673cae
FG
748 std::string error_message;
749 o = 0;
c07f9fc5
FG
750 for (const auto& opt_iter: schema) {
751 const Option &opt = opt_iter.second;
7c673cae 752 ostringstream err;
7c673cae 753 std::string as_option("--");
c07f9fc5
FG
754 as_option += opt.name;
755 option_name = opt.name;
11fdf7f2
TL
756 if (ceph_argparse_witharg(
757 args, i, &val, err,
758 string(string("--default-") + opt.name).c_str(), (char*)NULL)) {
759 if (!err.str().empty()) {
760 error_message = err.str();
761 ret = -EINVAL;
762 break;
763 }
764 ret = _set_val(values, tracker, val, opt, CONF_DEFAULT, &error_message);
765 break;
766 } else if (opt.type == Option::TYPE_BOOL) {
7c673cae
FG
767 int res;
768 if (ceph_argparse_binary_flag(args, i, &res, oss, as_option.c_str(),
769 (char*)NULL)) {
770 if (res == 0)
11fdf7f2 771 ret = _set_val(values, tracker, "false", opt, level, &error_message);
7c673cae 772 else if (res == 1)
11fdf7f2 773 ret = _set_val(values, tracker, "true", opt, level, &error_message);
7c673cae
FG
774 else
775 ret = res;
776 break;
777 } else {
778 std::string no("--no-");
c07f9fc5 779 no += opt.name;
7c673cae 780 if (ceph_argparse_flag(args, i, no.c_str(), (char*)NULL)) {
11fdf7f2 781 ret = _set_val(values, tracker, "false", opt, level, &error_message);
7c673cae
FG
782 break;
783 }
784 }
785 } else if (ceph_argparse_witharg(args, i, &val, err,
786 as_option.c_str(), (char*)NULL)) {
787 if (!err.str().empty()) {
788 error_message = err.str();
789 ret = -EINVAL;
790 break;
791 }
11fdf7f2 792 ret = _set_val(values, tracker, val, opt, level, &error_message);
7c673cae
FG
793 break;
794 }
795 ++o;
796 }
797
11fdf7f2
TL
798 if (ret < 0 || !error_message.empty()) {
799 ceph_assert(!option_name.empty());
7c673cae
FG
800 if (oss) {
801 *oss << "Parse error setting " << option_name << " to '"
802 << val << "' using injectargs";
803 if (!error_message.empty()) {
804 *oss << " (" << error_message << ")";
805 }
806 *oss << ".\n";
807 } else {
808 cerr << "parse error setting '" << option_name << "' to '"
809 << val << "'";
810 if (!error_message.empty()) {
811 cerr << " (" << error_message << ")";
812 }
813 cerr << "\n" << std::endl;
814 }
815 }
816
c07f9fc5 817 if (o == schema.size()) {
7c673cae
FG
818 // ignore
819 ++i;
820 }
11fdf7f2 821 return ret >= 0 ? 0 : ret;
7c673cae
FG
822}
823
11fdf7f2
TL
824int md_config_t::parse_injectargs(ConfigValues& values,
825 const ConfigTracker& tracker,
826 std::vector<const char*>& args,
7c673cae
FG
827 std::ostream *oss)
828{
7c673cae
FG
829 int ret = 0;
830 for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
11fdf7f2 831 int r = parse_option(values, tracker, args, i, oss, CONF_OVERRIDE);
7c673cae
FG
832 if (r < 0)
833 ret = r;
834 }
835 return ret;
836}
837
11fdf7f2 838void md_config_t::set_safe_to_start_threads()
7c673cae 839{
11fdf7f2 840 safe_to_start_threads = true;
7c673cae
FG
841}
842
11fdf7f2 843void md_config_t::_clear_safe_to_start_threads()
7c673cae 844{
11fdf7f2 845 safe_to_start_threads = false;
7c673cae
FG
846}
847
11fdf7f2
TL
848int md_config_t::injectargs(ConfigValues& values,
849 const ConfigTracker& tracker,
850 const std::string& s, std::ostream *oss)
7c673cae
FG
851{
852 int ret;
11fdf7f2
TL
853 char b[s.length()+1];
854 strcpy(b, s.c_str());
855 std::vector<const char*> nargs;
856 char *p = b;
857 while (*p) {
858 nargs.push_back(p);
859 while (*p && *p != ' ') p++;
860 if (!*p)
861 break;
862 *p++ = 0;
863 while (*p && *p == ' ') p++;
864 }
865 ret = parse_injectargs(values, tracker, nargs, oss);
866 if (!nargs.empty()) {
867 *oss << " failed to parse arguments: ";
868 std::string prefix;
869 for (std::vector<const char*>::const_iterator i = nargs.begin();
870 i != nargs.end(); ++i) {
871 *oss << prefix << *i;
872 prefix = ",";
7c673cae 873 }
11fdf7f2
TL
874 *oss << "\n";
875 ret = -EINVAL;
7c673cae 876 }
11fdf7f2 877 update_legacy_vals(values);
7c673cae
FG
878 return ret;
879}
880
11fdf7f2
TL
881void md_config_t::set_val_or_die(ConfigValues& values,
882 const ConfigTracker& tracker,
883 const std::string &key,
884 const std::string &val)
7c673cae 885{
c07f9fc5 886 std::stringstream err;
11fdf7f2 887 int ret = set_val(values, tracker, key, val, &err);
c07f9fc5
FG
888 if (ret != 0) {
889 std::cerr << "set_val_or_die(" << key << "): " << err.str();
7c673cae 890 }
11fdf7f2 891 ceph_assert(ret == 0);
7c673cae
FG
892}
893
11fdf7f2
TL
894int md_config_t::set_val(ConfigValues& values,
895 const ConfigTracker& tracker,
896 const std::string &key, const char *val,
897 std::stringstream *err_ss)
7c673cae 898{
c07f9fc5
FG
899 if (key.empty()) {
900 if (err_ss) *err_ss << "No key specified";
7c673cae 901 return -EINVAL;
c07f9fc5
FG
902 }
903 if (!val) {
7c673cae 904 return -EINVAL;
c07f9fc5 905 }
7c673cae
FG
906
907 std::string v(val);
7c673cae
FG
908
909 string k(ConfFile::normalize_key_name(key));
910
c07f9fc5
FG
911 const auto &opt_iter = schema.find(k);
912 if (opt_iter != schema.end()) {
913 const Option &opt = opt_iter->second;
7c673cae 914 std::string error_message;
11fdf7f2
TL
915 int r = _set_val(values, tracker, v, opt, CONF_OVERRIDE, &error_message);
916 if (r >= 0) {
c07f9fc5 917 if (err_ss) *err_ss << "Set " << opt.name << " to " << v;
11fdf7f2 918 r = 0;
c07f9fc5
FG
919 } else {
920 if (err_ss) *err_ss << error_message;
921 }
7c673cae
FG
922 return r;
923 }
924
c07f9fc5 925 if (err_ss) *err_ss << "Configuration option not found: '" << key << "'";
7c673cae
FG
926 return -ENOENT;
927}
928
11fdf7f2
TL
929int md_config_t::rm_val(ConfigValues& values, const std::string& key)
930{
931 return _rm_val(values, key, CONF_OVERRIDE);
932}
933
934void md_config_t::get_defaults_bl(const ConfigValues& values,
935 bufferlist *bl)
936{
937 if (defaults_bl.length() == 0) {
938 uint32_t n = 0;
939 bufferlist bl;
940 for (const auto &i : schema) {
941 ++n;
942 encode(i.second.name, bl);
943 auto [value, found] = values.get_value(i.second.name, CONF_DEFAULT);
944 if (found) {
945 encode(Option::to_str(value), bl);
946 } else {
947 string val;
948 conf_stringify(_get_val_default(i.second), &val);
949 encode(val, bl);
950 }
951 }
952 encode(n, defaults_bl);
953 defaults_bl.claim_append(bl);
954 }
955 *bl = defaults_bl;
956}
957
958void md_config_t::get_config_bl(
959 const ConfigValues& values,
960 uint64_t have_version,
961 bufferlist *bl,
962 uint64_t *got_version)
963{
964 if (values_bl.length() == 0) {
965 uint32_t n = 0;
966 bufferlist bl;
967 values.for_each([&](auto& name, auto& configs) {
968 if (name == "fsid" ||
969 name == "host") {
970 return;
971 }
972 ++n;
973 encode(name, bl);
974 encode((uint32_t)configs.size(), bl);
975 for (auto& j : configs) {
976 encode(j.first, bl);
977 encode(Option::to_str(j.second), bl);
978 }
979 });
980 // make sure overridden items appear, and include the default value
981 for (auto& i : ignored_mon_values) {
982 if (values.contains(i.first)) {
983 continue;
984 }
985 if (i.first == "fsid" ||
986 i.first == "host") {
987 continue;
988 }
989 const Option *opt = find_option(i.first);
990 if (!opt) {
991 continue;
992 }
993 ++n;
994 encode(i.first, bl);
995 encode((uint32_t)1, bl);
996 encode((int32_t)CONF_DEFAULT, bl);
997 string val;
998 conf_stringify(_get_val_default(*opt), &val);
999 encode(val, bl);
1000 }
1001 encode(n, values_bl);
1002 values_bl.claim_append(bl);
1003 encode(ignored_mon_values, values_bl);
1004 ++values_bl_version;
1005 }
1006 if (have_version != values_bl_version) {
1007 *bl = values_bl;
1008 *got_version = values_bl_version;
1009 }
1010}
7c673cae 1011
11fdf7f2
TL
1012int md_config_t::get_val(const ConfigValues& values,
1013 const std::string &key, char **buf, int len) const
7c673cae 1014{
11fdf7f2
TL
1015 string k(ConfFile::normalize_key_name(key));
1016 return _get_val_cstr(values, k, buf, len);
7c673cae
FG
1017}
1018
11fdf7f2
TL
1019int md_config_t::get_val(
1020 const ConfigValues& values,
1021 const std::string &key,
1022 std::string *val) const
7c673cae 1023{
11fdf7f2 1024 return conf_stringify(get_val_generic(values, key), val);
7c673cae
FG
1025}
1026
11fdf7f2
TL
1027Option::value_t md_config_t::get_val_generic(
1028 const ConfigValues& values,
1029 const std::string &key) const
7c673cae 1030{
11fdf7f2
TL
1031 string k(ConfFile::normalize_key_name(key));
1032 return _get_val(values, k);
1033}
7c673cae 1034
11fdf7f2
TL
1035Option::value_t md_config_t::_get_val(
1036 const ConfigValues& values,
1037 const std::string &key,
1038 expand_stack_t *stack,
1039 std::ostream *err) const
1040{
c07f9fc5
FG
1041 if (key.empty()) {
1042 return Option::value_t(boost::blank());
1043 }
7c673cae
FG
1044
1045 // In key names, leading and trailing whitespace are not significant.
1046 string k(ConfFile::normalize_key_name(key));
1047
11fdf7f2
TL
1048 const Option *o = find_option(key);
1049 if (!o) {
1050 // not a valid config option
c07f9fc5 1051 return Option::value_t(boost::blank());
7c673cae 1052 }
11fdf7f2
TL
1053
1054 return _get_val(values, *o, stack, err);
7c673cae
FG
1055}
1056
11fdf7f2
TL
1057Option::value_t md_config_t::_get_val(
1058 const ConfigValues& values,
1059 const Option& o,
1060 expand_stack_t *stack,
1061 std::ostream *err) const
1062{
1063 expand_stack_t a_stack;
1064 if (!stack) {
1065 stack = &a_stack;
1066 }
1067 return _expand_meta(values,
1068 _get_val_nometa(values, o),
1069 &o, stack, err);
1070}
7c673cae 1071
11fdf7f2
TL
1072Option::value_t md_config_t::_get_val_nometa(const ConfigValues& values,
1073 const Option& o) const
1074{
1075 if (auto [value, found] = values.get_value(o.name, -1); found) {
1076 return value;
1077 } else {
1078 return _get_val_default(o);
1079 }
1080}
1081
1082const Option::value_t& md_config_t::_get_val_default(const Option& o) const
1083{
1084 bool has_daemon_default = !boost::get<boost::blank>(&o.daemon_value);
1085 if (is_daemon && has_daemon_default) {
1086 return o.daemon_value;
1087 } else {
1088 return o.value;
1089 }
1090}
1091
1092void md_config_t::early_expand_meta(
1093 const ConfigValues& values,
1094 std::string &val,
1095 std::ostream *err) const
1096{
1097 expand_stack_t stack;
1098 Option::value_t v = _expand_meta(values,
1099 Option::value_t(val),
1100 nullptr, &stack, err);
1101 conf_stringify(v, &val);
1102}
1103
1104bool md_config_t::finalize_reexpand_meta(ConfigValues& values,
1105 const ConfigTracker& tracker)
1106{
1107 for (auto& [name, value] : may_reexpand_meta) {
1108 set_val(values, tracker, name, value);
1109 }
1110
1111 if (!may_reexpand_meta.empty()) {
1112 // meta expands could have modified anything. Copy it all out again.
1113 update_legacy_vals(values);
1114 return true;
1115 } else {
1116 return false;
1117 }
1118}
1119
1120Option::value_t md_config_t::_expand_meta(
1121 const ConfigValues& values,
1122 const Option::value_t& in,
1123 const Option *o,
1124 expand_stack_t *stack,
1125 std::ostream *err) const
1126{
1127 //cout << __func__ << " in '" << in << "' stack " << stack << std::endl;
1128 if (!stack) {
1129 return in;
1130 }
1131 const std::string *str = boost::get<const std::string>(&in);
1132 if (!str) {
1133 // strings only!
1134 return in;
1135 }
1136
1137 auto pos = str->find('$');
1138 if (pos == std::string::npos) {
1139 // no substitutions!
1140 return in;
1141 }
1142
1143 if (o) {
1144 stack->push_back(make_pair(o, &in));
1145 }
1146 string out;
1147 decltype(pos) last_pos = 0;
1148 while (pos != std::string::npos) {
1149 ceph_assert((*str)[pos] == '$');
1150 if (pos > last_pos) {
1151 out += str->substr(last_pos, pos - last_pos);
1152 }
1153
1154 // try to parse the variable name into var, either \$\{(.+)\} or
1155 // \$[a-z\_]+
1156 const char *valid_chars = "abcdefghijklmnopqrstuvwxyz_";
1157 string var;
1158 size_t endpos = 0;
1159 if ((*str)[pos+1] == '{') {
1160 // ...${foo_bar}...
1161 endpos = str->find_first_not_of(valid_chars, pos + 2);
1162 if (endpos != std::string::npos &&
1163 (*str)[endpos] == '}') {
1164 var = str->substr(pos + 2, endpos - pos - 2);
1165 endpos++;
1166 }
7c673cae 1167 } else {
11fdf7f2
TL
1168 // ...$foo...
1169 endpos = str->find_first_not_of(valid_chars, pos + 1);
1170 if (endpos != std::string::npos)
1171 var = str->substr(pos + 1, endpos - pos - 1);
1172 else
1173 var = str->substr(pos + 1);
7c673cae 1174 }
11fdf7f2
TL
1175 last_pos = endpos;
1176
1177 if (!var.size()) {
1178 out += '$';
1179 } else {
1180 //cout << " found var " << var << std::endl;
1181 // special metavariable?
1182 if (var == "type") {
1183 out += values.name.get_type_name();
1184 } else if (var == "cluster") {
1185 out += values.cluster;
1186 } else if (var == "name") {
1187 out += values.name.to_cstr();
1188 } else if (var == "host") {
1189 if (values.host == "") {
1190 out += ceph_get_short_hostname();
1191 } else {
1192 out += values.host;
1193 }
1194 } else if (var == "num") {
1195 out += values.name.get_id().c_str();
1196 } else if (var == "id") {
1197 out += values.name.get_id();
1198 } else if (var == "pid") {
1199 out += stringify(getpid());
1200 if (o) {
1201 may_reexpand_meta[o->name] = *str;
1202 }
1203 } else if (var == "cctid") {
1204 out += stringify((unsigned long long)this);
1205 } else if (var == "home") {
1206 const char *home = getenv("HOME");
1207 out = home ? std::string(home) : std::string();
1208 } else {
1209 if (var == "data_dir") {
1210 var = data_dir_option;
1211 }
1212 const Option *o = find_option(var);
1213 if (!o) {
1214 out += str->substr(pos, endpos - pos);
1215 } else {
1216 auto match = std::find_if(
1217 stack->begin(), stack->end(),
1218 [o](pair<const Option *,const Option::value_t*>& item) {
1219 return item.first == o;
1220 });
1221 if (match != stack->end()) {
1222 // substitution loop; break the cycle
1223 if (err) {
1224 *err << "variable expansion loop at " << var << "="
1225 << Option::to_str(*match->second) << "\n"
1226 << "expansion stack:\n";
1227 for (auto i = stack->rbegin(); i != stack->rend(); ++i) {
1228 *err << i->first->name << "="
1229 << Option::to_str(*i->second) << "\n";
1230 }
1231 }
1232 return Option::value_t(std::string("$") + o->name);
1233 } else {
1234 // recursively evaluate!
1235 string n;
1236 conf_stringify(_get_val(values, *o, stack, err), &n);
1237 out += n;
1238 }
1239 }
1240 }
1241 }
1242 pos = str->find('$', last_pos);
7c673cae 1243 }
11fdf7f2
TL
1244 if (last_pos != std::string::npos) {
1245 out += str->substr(last_pos);
1246 }
1247 if (o) {
1248 stack->pop_back();
1249 }
1250
1251 return Option::value_t(out);
7c673cae
FG
1252}
1253
11fdf7f2
TL
1254int md_config_t::_get_val_cstr(
1255 const ConfigValues& values,
1256 const std::string &key, char **buf, int len) const
7c673cae 1257{
c07f9fc5 1258 if (key.empty())
7c673cae
FG
1259 return -EINVAL;
1260
11fdf7f2
TL
1261 string val;
1262 if (conf_stringify(_get_val(values, key), &val) == 0) {
224ce89b 1263 int l = val.length() + 1;
7c673cae
FG
1264 if (len == -1) {
1265 *buf = (char*)malloc(l);
1266 if (!*buf)
1267 return -ENOMEM;
224ce89b 1268 strncpy(*buf, val.c_str(), l);
7c673cae
FG
1269 return 0;
1270 }
224ce89b 1271 snprintf(*buf, len, "%s", val.c_str());
7c673cae
FG
1272 return (l > len) ? -ENAMETOOLONG : 0;
1273 }
1274
224ce89b 1275 string k(ConfFile::normalize_key_name(key));
7c673cae
FG
1276
1277 // couldn't find a configuration option with key 'k'
1278 return -ENOENT;
1279}
1280
1281void md_config_t::get_all_keys(std::vector<std::string> *keys) const {
1282 const std::string negative_flag_prefix("no_");
1283
1284 keys->clear();
c07f9fc5
FG
1285 keys->reserve(schema.size());
1286 for (const auto &i: schema) {
1287 const Option &opt = i.second;
7c673cae 1288 keys->push_back(opt.name);
c07f9fc5 1289 if (opt.type == Option::TYPE_BOOL) {
7c673cae
FG
1290 keys->push_back(negative_flag_prefix + opt.name);
1291 }
1292 }
7c673cae
FG
1293}
1294
1295/* The order of the sections here is important. The first section in the
1296 * vector is the "highest priority" section; if we find it there, we'll stop
1297 * looking. The lowest priority section is the one we look in only if all
1298 * others had nothing. This should always be the global section.
1299 */
11fdf7f2
TL
1300void md_config_t::get_my_sections(const ConfigValues& values,
1301 std::vector <std::string> &sections) const
7c673cae 1302{
11fdf7f2 1303 _get_my_sections(values, sections);
7c673cae
FG
1304}
1305
11fdf7f2
TL
1306void md_config_t::_get_my_sections(const ConfigValues& values,
1307 std::vector <std::string> &sections) const
7c673cae 1308{
11fdf7f2 1309 sections.push_back(values.name.to_str());
7c673cae 1310
11fdf7f2 1311 sections.push_back(values.name.get_type_name());
7c673cae
FG
1312
1313 sections.push_back("global");
1314}
1315
1316// Return a list of all sections
1317int md_config_t::get_all_sections(std::vector <std::string> &sections) const
1318{
7c673cae
FG
1319 for (ConfFile::const_section_iter_t s = cf.sections_begin();
1320 s != cf.sections_end(); ++s) {
1321 sections.push_back(s->first);
1322 }
1323 return 0;
1324}
1325
11fdf7f2
TL
1326int md_config_t::get_val_from_conf_file(
1327 const ConfigValues& values,
1328 const std::vector <std::string> &sections,
1329 const std::string &key,
1330 std::string &out,
1331 bool emeta) const
7c673cae 1332{
11fdf7f2
TL
1333 int r = _get_val_from_conf_file(sections, key, out);
1334 if (r < 0) {
1335 return r;
1336 }
1337 if (emeta) {
1338 expand_stack_t stack;
1339 auto v = _expand_meta(values, Option::value_t(out), nullptr, &stack, nullptr);
1340 conf_stringify(v, &out);
1341 }
1342 return 0;
7c673cae
FG
1343}
1344
11fdf7f2
TL
1345int md_config_t::_get_val_from_conf_file(
1346 const std::vector <std::string> &sections,
1347 const std::string &key,
1348 std::string &out) const
7c673cae 1349{
7c673cae
FG
1350 std::vector <std::string>::const_iterator s = sections.begin();
1351 std::vector <std::string>::const_iterator s_end = sections.end();
1352 for (; s != s_end; ++s) {
1353 int ret = cf.read(s->c_str(), key, out);
1354 if (ret == 0) {
7c673cae 1355 return 0;
11fdf7f2 1356 } else if (ret != -ENOENT) {
7c673cae 1357 return ret;
11fdf7f2 1358 }
7c673cae
FG
1359 }
1360 return -ENOENT;
1361}
1362
11fdf7f2
TL
1363int md_config_t::_set_val(
1364 ConfigValues& values,
1365 const ConfigTracker& observers,
1366 const std::string &raw_val,
1367 const Option &opt,
1368 int level,
1369 std::string *error_message)
7c673cae 1370{
11fdf7f2 1371 Option::value_t new_value;
81eedcae 1372 ceph_assert(error_message);
11fdf7f2
TL
1373 int r = opt.parse_value(raw_val, &new_value, error_message);
1374 if (r < 0) {
c07f9fc5
FG
1375 return r;
1376 }
7c673cae 1377
11fdf7f2
TL
1378 // unsafe runtime change?
1379 if (!opt.can_update_at_runtime() &&
1380 safe_to_start_threads &&
1381 !observers.is_tracking(opt.name)) {
1382 // accept value if it is not actually a change
1383 if (new_value != _get_val_nometa(values, opt)) {
1384 *error_message = string("Configuration option '") + opt.name +
1385 "' may not be modified at runtime";
1386 return -ENOSYS;
c07f9fc5 1387 }
7c673cae 1388 }
7c673cae 1389
c07f9fc5 1390 // Apply the value to its entry in the `values` map
11fdf7f2
TL
1391 auto result = values.set_value(opt.name, std::move(new_value), level);
1392 switch (result) {
1393 case ConfigValues::SET_NO_CHANGE:
1394 break;
1395 case ConfigValues::SET_NO_EFFECT:
1396 values_bl.clear();
1397 break;
1398 case ConfigValues::SET_HAVE_EFFECT:
1399 values_bl.clear();
1400 _refresh(values, opt);
1401 break;
1402 }
1403 return result;
1404}
7c673cae 1405
11fdf7f2
TL
1406void md_config_t::_refresh(ConfigValues& values, const Option& opt)
1407{
c07f9fc5
FG
1408 // Apply the value to its legacy field, if it has one
1409 auto legacy_ptr_iter = legacy_values.find(std::string(opt.name));
1410 if (legacy_ptr_iter != legacy_values.end()) {
11fdf7f2
TL
1411 update_legacy_val(values, opt, legacy_ptr_iter->second);
1412 }
1413 // Was this a debug_* option update?
1414 if (opt.subsys >= 0) {
1415 string actual_val;
1416 conf_stringify(_get_val(values, opt), &actual_val);
1417 values.set_logging(opt.subsys, actual_val.c_str());
1418 } else {
1419 // normal option, advertise the change.
1420 values.changed.insert(opt.name);
7c673cae 1421 }
11fdf7f2 1422}
7c673cae 1423
11fdf7f2
TL
1424int md_config_t::_rm_val(ConfigValues& values,
1425 const std::string& key,
1426 int level)
1427{
1428 if (schema.count(key) == 0) {
1429 return -EINVAL;
1430 }
1431 auto ret = values.rm_val(key, level);
1432 if (ret < 0) {
1433 return ret;
1434 }
1435 if (ret == ConfigValues::SET_HAVE_EFFECT) {
1436 _refresh(values, *find_option(key));
1437 }
1438 values_bl.clear();
c07f9fc5
FG
1439 return 0;
1440}
7c673cae 1441
11fdf7f2
TL
1442namespace {
1443template<typename Size>
1444struct get_size_visitor : public boost::static_visitor<Size>
1445{
1446 get_size_visitor() {}
1447
1448 template<typename T>
1449 Size operator()(const T&) const {
1450 return -1;
1451 }
1452 Size operator()(const Option::size_t& sz) const {
1453 return static_cast<Size>(sz.value);
1454 }
1455 Size operator()(const Size& v) const {
1456 return v;
1457 }
1458};
1459
c07f9fc5
FG
1460/**
1461 * Handles assigning from a variant-of-types to a variant-of-pointers-to-types
1462 */
11fdf7f2 1463template<class Config>
c07f9fc5
FG
1464class assign_visitor : public boost::static_visitor<>
1465{
11fdf7f2 1466 Config *conf;
c07f9fc5
FG
1467 Option::value_t val;
1468 public:
7c673cae 1469
11fdf7f2 1470 assign_visitor(Config *conf_, Option::value_t val_)
c07f9fc5
FG
1471 : conf(conf_), val(val_)
1472 {}
1473
1474 template <typename T>
11fdf7f2 1475 void operator()(T Config::* ptr) const
c07f9fc5 1476 {
11fdf7f2 1477 T *member = const_cast<T *>(&(conf->*(boost::get<const T Config::*>(ptr))));
c07f9fc5
FG
1478
1479 *member = boost::get<T>(val);
7c673cae 1480 }
11fdf7f2
TL
1481 void operator()(uint64_t Config::* ptr) const
1482 {
1483 using T = uint64_t;
1484 auto member = const_cast<T*>(&(conf->*(boost::get<const T Config::*>(ptr))));
1485 *member = boost::apply_visitor(get_size_visitor<T>{}, val);
1486 }
1487 void operator()(int64_t Config::* ptr) const
1488 {
1489 using T = int64_t;
1490 auto member = const_cast<T*>(&(conf->*(boost::get<const T Config::*>(ptr))));
1491 *member = boost::apply_visitor(get_size_visitor<T>{}, val);
c07f9fc5 1492 }
7c673cae 1493};
11fdf7f2 1494} // anonymous namespace
7c673cae 1495
11fdf7f2 1496void md_config_t::update_legacy_vals(ConfigValues& values)
7c673cae 1497{
11fdf7f2
TL
1498 for (const auto &i : legacy_values) {
1499 const auto &name = i.first;
1500 const auto &option = schema.at(name);
1501 auto ptr = i.second;
1502 update_legacy_val(values, option, ptr);
7c673cae 1503 }
7c673cae
FG
1504}
1505
11fdf7f2
TL
1506void md_config_t::update_legacy_val(ConfigValues& values,
1507 const Option &opt,
1508 md_config_t::member_ptr_t member_ptr)
7c673cae 1509{
11fdf7f2
TL
1510 Option::value_t v = _get_val(values, opt);
1511 boost::apply_visitor(assign_visitor(&values, v), member_ptr);
7c673cae
FG
1512}
1513
11fdf7f2 1514static void dump(Formatter *f, int level, Option::value_t in)
7c673cae 1515{
11fdf7f2
TL
1516 if (const bool *v = boost::get<const bool>(&in)) {
1517 f->dump_bool(ceph_conf_level_name(level), *v);
1518 } else if (const int64_t *v = boost::get<const int64_t>(&in)) {
1519 f->dump_int(ceph_conf_level_name(level), *v);
1520 } else if (const uint64_t *v = boost::get<const uint64_t>(&in)) {
1521 f->dump_unsigned(ceph_conf_level_name(level), *v);
1522 } else if (const double *v = boost::get<const double>(&in)) {
1523 f->dump_float(ceph_conf_level_name(level), *v);
1524 } else {
1525 f->dump_stream(ceph_conf_level_name(level)) << Option::to_str(in);
7c673cae 1526 }
7c673cae
FG
1527}
1528
1529void md_config_t::diff(
11fdf7f2
TL
1530 const ConfigValues& values,
1531 Formatter *f,
1532 string name) const
7c673cae 1533{
11fdf7f2 1534 values.for_each([this, f, &values] (auto& name, auto& configs) {
494da23a 1535 if (configs.empty()) {
11fdf7f2 1536 return;
7c673cae 1537 }
11fdf7f2
TL
1538 f->open_object_section(name.c_str());
1539 const Option *o = find_option(name);
494da23a
TL
1540 if (configs.size() &&
1541 configs.begin()->first != CONF_DEFAULT) {
1542 // show compiled-in default only if an override default wasn't provided
1543 dump(f, CONF_DEFAULT, _get_val_default(*o));
1544 }
11fdf7f2
TL
1545 for (auto& j : configs) {
1546 dump(f, j.first, j.second);
31f18b77 1547 }
11fdf7f2
TL
1548 dump(f, CONF_FINAL, _get_val(values, *o));
1549 f->close_section();
1550 });
7c673cae
FG
1551}
1552
1553void md_config_t::complain_about_parse_errors(CephContext *cct)
1554{
1555 ::complain_about_parse_errors(cct, &parse_errors);
1556}