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