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