]> git.proxmox.com Git - ceph.git/blame - ceph/src/common/config.cc
import 15.2.0 Octopus source
[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)) {
11fdf7f2
TL
662 set_val_or_die(values, tracker, "daemonize", "false");
663 set_val_or_die(values, tracker, "log_file", "");
664 set_val_or_die(values, tracker, "log_to_stderr", "true");
665 set_val_or_die(values, tracker, "err_to_stderr", "true");
666 set_val_or_die(values, tracker, "log_to_syslog", "false");
7c673cae
FG
667 }
668 // Some stuff that we wanted to give universal single-character options for
669 // Careful: you can burn through the alphabet pretty quickly by adding
670 // to this list.
671 else if (ceph_argparse_witharg(args, i, &val, "--monmap", "-M", (char*)NULL)) {
11fdf7f2 672 set_val_or_die(values, tracker, "monmap", val.c_str());
7c673cae
FG
673 }
674 else if (ceph_argparse_witharg(args, i, &val, "--mon_host", "-m", (char*)NULL)) {
11fdf7f2 675 set_val_or_die(values, tracker, "mon_host", val.c_str());
7c673cae
FG
676 }
677 else if (ceph_argparse_witharg(args, i, &val, "--bind", (char*)NULL)) {
11fdf7f2 678 set_val_or_die(values, tracker, "public_addr", val.c_str());
7c673cae
FG
679 }
680 else if (ceph_argparse_witharg(args, i, &val, "--keyfile", "-K", (char*)NULL)) {
11fdf7f2
TL
681 bufferlist bl;
682 string err;
683 int r;
684 if (val == "-") {
685 r = bl.read_fd(STDIN_FILENO, 1024);
686 } else {
687 r = bl.read_file(val.c_str(), &err);
688 }
689 if (r >= 0) {
690 string k(bl.c_str(), bl.length());
691 set_val_or_die(values, tracker, "key", k.c_str());
692 }
7c673cae
FG
693 }
694 else if (ceph_argparse_witharg(args, i, &val, "--keyring", "-k", (char*)NULL)) {
11fdf7f2 695 set_val_or_die(values, tracker, "keyring", val.c_str());
7c673cae
FG
696 }
697 else if (ceph_argparse_witharg(args, i, &val, "--client_mountpoint", "-r", (char*)NULL)) {
11fdf7f2 698 set_val_or_die(values, tracker, "client_mountpoint", val.c_str());
7c673cae
FG
699 }
700 else {
11fdf7f2 701 int r = parse_option(values, tracker, args, i, NULL, level);
3efd9988
FG
702 if (r < 0) {
703 return r;
704 }
7c673cae
FG
705 }
706 }
11fdf7f2
TL
707 // meta expands could have modified anything. Copy it all out again.
708 update_legacy_vals(values);
709 return 0;
710}
711
712void md_config_t::do_argv_commands(const ConfigValues& values) const
713{
7c673cae 714
11fdf7f2
TL
715 if (do_show_config) {
716 _show_config(values, &cout, NULL);
7c673cae
FG
717 _exit(0);
718 }
719
11fdf7f2
TL
720 if (do_show_config_value.size()) {
721 string val;
722 int r = conf_stringify(_get_val(values, do_show_config_value, 0, &cerr),
723 &val);
7c673cae
FG
724 if (r < 0) {
725 if (r == -ENOENT)
11fdf7f2
TL
726 std::cerr << "failed to get config option '"
727 << do_show_config_value << "': option not found" << std::endl;
7c673cae 728 else
11fdf7f2
TL
729 std::cerr << "failed to get config option '"
730 << do_show_config_value << "': " << cpp_strerror(r)
731 << std::endl;
7c673cae
FG
732 _exit(1);
733 }
11fdf7f2 734 std::cout << val << std::endl;
7c673cae
FG
735 _exit(0);
736 }
7c673cae
FG
737}
738
11fdf7f2
TL
739int md_config_t::parse_option(ConfigValues& values,
740 const ConfigTracker& tracker,
741 std::vector<const char*>& args,
742 std::vector<const char*>::iterator& i,
743 ostream *oss,
744 int level)
7c673cae
FG
745{
746 int ret = 0;
c07f9fc5 747 size_t o = 0;
7c673cae
FG
748 std::string val;
749
c07f9fc5 750 std::string option_name;
7c673cae
FG
751 std::string error_message;
752 o = 0;
c07f9fc5
FG
753 for (const auto& opt_iter: schema) {
754 const Option &opt = opt_iter.second;
7c673cae 755 ostringstream err;
7c673cae 756 std::string as_option("--");
c07f9fc5
FG
757 as_option += opt.name;
758 option_name = opt.name;
11fdf7f2
TL
759 if (ceph_argparse_witharg(
760 args, i, &val, err,
761 string(string("--default-") + opt.name).c_str(), (char*)NULL)) {
762 if (!err.str().empty()) {
763 error_message = err.str();
764 ret = -EINVAL;
765 break;
766 }
767 ret = _set_val(values, tracker, val, opt, CONF_DEFAULT, &error_message);
768 break;
769 } else if (opt.type == Option::TYPE_BOOL) {
7c673cae
FG
770 int res;
771 if (ceph_argparse_binary_flag(args, i, &res, oss, as_option.c_str(),
772 (char*)NULL)) {
773 if (res == 0)
11fdf7f2 774 ret = _set_val(values, tracker, "false", opt, level, &error_message);
7c673cae 775 else if (res == 1)
11fdf7f2 776 ret = _set_val(values, tracker, "true", opt, level, &error_message);
7c673cae
FG
777 else
778 ret = res;
779 break;
780 } else {
781 std::string no("--no-");
c07f9fc5 782 no += opt.name;
7c673cae 783 if (ceph_argparse_flag(args, i, no.c_str(), (char*)NULL)) {
11fdf7f2 784 ret = _set_val(values, tracker, "false", opt, level, &error_message);
7c673cae
FG
785 break;
786 }
787 }
788 } else if (ceph_argparse_witharg(args, i, &val, err,
789 as_option.c_str(), (char*)NULL)) {
790 if (!err.str().empty()) {
791 error_message = err.str();
792 ret = -EINVAL;
793 break;
794 }
11fdf7f2 795 ret = _set_val(values, tracker, val, opt, level, &error_message);
7c673cae
FG
796 break;
797 }
798 ++o;
799 }
800
11fdf7f2
TL
801 if (ret < 0 || !error_message.empty()) {
802 ceph_assert(!option_name.empty());
7c673cae
FG
803 if (oss) {
804 *oss << "Parse error setting " << option_name << " to '"
805 << val << "' using injectargs";
806 if (!error_message.empty()) {
807 *oss << " (" << error_message << ")";
808 }
809 *oss << ".\n";
810 } else {
811 cerr << "parse error setting '" << option_name << "' to '"
812 << val << "'";
813 if (!error_message.empty()) {
814 cerr << " (" << error_message << ")";
815 }
816 cerr << "\n" << std::endl;
817 }
818 }
819
c07f9fc5 820 if (o == schema.size()) {
7c673cae
FG
821 // ignore
822 ++i;
823 }
11fdf7f2 824 return ret >= 0 ? 0 : ret;
7c673cae
FG
825}
826
11fdf7f2
TL
827int md_config_t::parse_injectargs(ConfigValues& values,
828 const ConfigTracker& tracker,
829 std::vector<const char*>& args,
7c673cae
FG
830 std::ostream *oss)
831{
7c673cae
FG
832 int ret = 0;
833 for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
11fdf7f2 834 int r = parse_option(values, tracker, args, i, oss, CONF_OVERRIDE);
7c673cae
FG
835 if (r < 0)
836 ret = r;
837 }
838 return ret;
839}
840
11fdf7f2 841void md_config_t::set_safe_to_start_threads()
7c673cae 842{
11fdf7f2 843 safe_to_start_threads = true;
7c673cae
FG
844}
845
11fdf7f2 846void md_config_t::_clear_safe_to_start_threads()
7c673cae 847{
11fdf7f2 848 safe_to_start_threads = false;
7c673cae
FG
849}
850
11fdf7f2
TL
851int md_config_t::injectargs(ConfigValues& values,
852 const ConfigTracker& tracker,
853 const std::string& s, std::ostream *oss)
7c673cae
FG
854{
855 int ret;
11fdf7f2
TL
856 char b[s.length()+1];
857 strcpy(b, s.c_str());
858 std::vector<const char*> nargs;
859 char *p = b;
860 while (*p) {
861 nargs.push_back(p);
862 while (*p && *p != ' ') p++;
863 if (!*p)
864 break;
865 *p++ = 0;
866 while (*p && *p == ' ') p++;
867 }
868 ret = parse_injectargs(values, tracker, nargs, oss);
869 if (!nargs.empty()) {
870 *oss << " failed to parse arguments: ";
871 std::string prefix;
872 for (std::vector<const char*>::const_iterator i = nargs.begin();
873 i != nargs.end(); ++i) {
874 *oss << prefix << *i;
875 prefix = ",";
7c673cae 876 }
11fdf7f2
TL
877 *oss << "\n";
878 ret = -EINVAL;
7c673cae 879 }
11fdf7f2 880 update_legacy_vals(values);
7c673cae
FG
881 return ret;
882}
883
11fdf7f2
TL
884void md_config_t::set_val_or_die(ConfigValues& values,
885 const ConfigTracker& tracker,
9f95a23c 886 const std::string_view key,
11fdf7f2 887 const std::string &val)
7c673cae 888{
c07f9fc5 889 std::stringstream err;
11fdf7f2 890 int ret = set_val(values, tracker, key, val, &err);
c07f9fc5
FG
891 if (ret != 0) {
892 std::cerr << "set_val_or_die(" << key << "): " << err.str();
7c673cae 893 }
11fdf7f2 894 ceph_assert(ret == 0);
7c673cae
FG
895}
896
11fdf7f2
TL
897int md_config_t::set_val(ConfigValues& values,
898 const ConfigTracker& tracker,
9f95a23c 899 const std::string_view key, const char *val,
11fdf7f2 900 std::stringstream *err_ss)
7c673cae 901{
c07f9fc5
FG
902 if (key.empty()) {
903 if (err_ss) *err_ss << "No key specified";
7c673cae 904 return -EINVAL;
c07f9fc5
FG
905 }
906 if (!val) {
7c673cae 907 return -EINVAL;
c07f9fc5 908 }
7c673cae
FG
909
910 std::string v(val);
7c673cae
FG
911
912 string k(ConfFile::normalize_key_name(key));
913
c07f9fc5
FG
914 const auto &opt_iter = schema.find(k);
915 if (opt_iter != schema.end()) {
916 const Option &opt = opt_iter->second;
7c673cae 917 std::string error_message;
11fdf7f2
TL
918 int r = _set_val(values, tracker, v, opt, CONF_OVERRIDE, &error_message);
919 if (r >= 0) {
c07f9fc5 920 if (err_ss) *err_ss << "Set " << opt.name << " to " << v;
11fdf7f2 921 r = 0;
c07f9fc5
FG
922 } else {
923 if (err_ss) *err_ss << error_message;
924 }
7c673cae
FG
925 return r;
926 }
927
c07f9fc5 928 if (err_ss) *err_ss << "Configuration option not found: '" << key << "'";
7c673cae
FG
929 return -ENOENT;
930}
931
9f95a23c 932int md_config_t::rm_val(ConfigValues& values, const std::string_view key)
11fdf7f2
TL
933{
934 return _rm_val(values, key, CONF_OVERRIDE);
935}
936
937void md_config_t::get_defaults_bl(const ConfigValues& values,
938 bufferlist *bl)
939{
940 if (defaults_bl.length() == 0) {
941 uint32_t n = 0;
942 bufferlist bl;
943 for (const auto &i : schema) {
944 ++n;
945 encode(i.second.name, bl);
946 auto [value, found] = values.get_value(i.second.name, CONF_DEFAULT);
947 if (found) {
948 encode(Option::to_str(value), bl);
949 } else {
950 string val;
951 conf_stringify(_get_val_default(i.second), &val);
952 encode(val, bl);
953 }
954 }
955 encode(n, defaults_bl);
956 defaults_bl.claim_append(bl);
957 }
958 *bl = defaults_bl;
959}
960
961void md_config_t::get_config_bl(
962 const ConfigValues& values,
963 uint64_t have_version,
964 bufferlist *bl,
965 uint64_t *got_version)
966{
967 if (values_bl.length() == 0) {
968 uint32_t n = 0;
969 bufferlist bl;
970 values.for_each([&](auto& name, auto& configs) {
971 if (name == "fsid" ||
972 name == "host") {
973 return;
974 }
975 ++n;
976 encode(name, bl);
977 encode((uint32_t)configs.size(), bl);
978 for (auto& j : configs) {
979 encode(j.first, bl);
980 encode(Option::to_str(j.second), bl);
981 }
982 });
983 // make sure overridden items appear, and include the default value
984 for (auto& i : ignored_mon_values) {
985 if (values.contains(i.first)) {
986 continue;
987 }
988 if (i.first == "fsid" ||
989 i.first == "host") {
990 continue;
991 }
992 const Option *opt = find_option(i.first);
993 if (!opt) {
994 continue;
995 }
996 ++n;
997 encode(i.first, bl);
998 encode((uint32_t)1, bl);
999 encode((int32_t)CONF_DEFAULT, bl);
1000 string val;
1001 conf_stringify(_get_val_default(*opt), &val);
1002 encode(val, bl);
1003 }
1004 encode(n, values_bl);
1005 values_bl.claim_append(bl);
1006 encode(ignored_mon_values, values_bl);
1007 ++values_bl_version;
1008 }
1009 if (have_version != values_bl_version) {
1010 *bl = values_bl;
1011 *got_version = values_bl_version;
1012 }
1013}
7c673cae 1014
11fdf7f2 1015int md_config_t::get_val(const ConfigValues& values,
9f95a23c 1016 const std::string_view key, char **buf, int len) const
7c673cae 1017{
11fdf7f2
TL
1018 string k(ConfFile::normalize_key_name(key));
1019 return _get_val_cstr(values, k, buf, len);
7c673cae
FG
1020}
1021
11fdf7f2
TL
1022int md_config_t::get_val(
1023 const ConfigValues& values,
9f95a23c 1024 const std::string_view key,
11fdf7f2 1025 std::string *val) const
7c673cae 1026{
11fdf7f2 1027 return conf_stringify(get_val_generic(values, key), val);
7c673cae
FG
1028}
1029
11fdf7f2
TL
1030Option::value_t md_config_t::get_val_generic(
1031 const ConfigValues& values,
9f95a23c 1032 const std::string_view key) const
7c673cae 1033{
9f95a23c 1034 return _get_val(values, key);
11fdf7f2 1035}
7c673cae 1036
11fdf7f2
TL
1037Option::value_t md_config_t::_get_val(
1038 const ConfigValues& values,
9f95a23c 1039 const std::string_view key,
11fdf7f2
TL
1040 expand_stack_t *stack,
1041 std::ostream *err) const
1042{
c07f9fc5
FG
1043 if (key.empty()) {
1044 return Option::value_t(boost::blank());
1045 }
7c673cae
FG
1046
1047 // In key names, leading and trailing whitespace are not significant.
1048 string k(ConfFile::normalize_key_name(key));
1049
9f95a23c 1050 const Option *o = find_option(k);
11fdf7f2
TL
1051 if (!o) {
1052 // not a valid config option
c07f9fc5 1053 return Option::value_t(boost::blank());
7c673cae 1054 }
11fdf7f2
TL
1055
1056 return _get_val(values, *o, stack, err);
7c673cae
FG
1057}
1058
11fdf7f2
TL
1059Option::value_t md_config_t::_get_val(
1060 const ConfigValues& values,
1061 const Option& o,
1062 expand_stack_t *stack,
1063 std::ostream *err) const
1064{
1065 expand_stack_t a_stack;
1066 if (!stack) {
1067 stack = &a_stack;
1068 }
1069 return _expand_meta(values,
1070 _get_val_nometa(values, o),
1071 &o, stack, err);
1072}
7c673cae 1073
11fdf7f2
TL
1074Option::value_t md_config_t::_get_val_nometa(const ConfigValues& values,
1075 const Option& o) const
1076{
1077 if (auto [value, found] = values.get_value(o.name, -1); found) {
1078 return value;
1079 } else {
1080 return _get_val_default(o);
1081 }
1082}
1083
1084const Option::value_t& md_config_t::_get_val_default(const Option& o) const
1085{
1086 bool has_daemon_default = !boost::get<boost::blank>(&o.daemon_value);
1087 if (is_daemon && has_daemon_default) {
1088 return o.daemon_value;
1089 } else {
1090 return o.value;
1091 }
1092}
1093
1094void md_config_t::early_expand_meta(
1095 const ConfigValues& values,
1096 std::string &val,
1097 std::ostream *err) const
1098{
1099 expand_stack_t stack;
1100 Option::value_t v = _expand_meta(values,
1101 Option::value_t(val),
1102 nullptr, &stack, err);
1103 conf_stringify(v, &val);
1104}
1105
1106bool md_config_t::finalize_reexpand_meta(ConfigValues& values,
1107 const ConfigTracker& tracker)
1108{
1109 for (auto& [name, value] : may_reexpand_meta) {
1110 set_val(values, tracker, name, value);
1111 }
1112
1113 if (!may_reexpand_meta.empty()) {
1114 // meta expands could have modified anything. Copy it all out again.
1115 update_legacy_vals(values);
1116 return true;
1117 } else {
1118 return false;
1119 }
1120}
1121
1122Option::value_t md_config_t::_expand_meta(
1123 const ConfigValues& values,
1124 const Option::value_t& in,
1125 const Option *o,
1126 expand_stack_t *stack,
1127 std::ostream *err) const
1128{
1129 //cout << __func__ << " in '" << in << "' stack " << stack << std::endl;
1130 if (!stack) {
1131 return in;
1132 }
1133 const std::string *str = boost::get<const std::string>(&in);
1134 if (!str) {
1135 // strings only!
1136 return in;
1137 }
1138
1139 auto pos = str->find('$');
1140 if (pos == std::string::npos) {
1141 // no substitutions!
1142 return in;
1143 }
1144
1145 if (o) {
1146 stack->push_back(make_pair(o, &in));
1147 }
1148 string out;
1149 decltype(pos) last_pos = 0;
1150 while (pos != std::string::npos) {
1151 ceph_assert((*str)[pos] == '$');
1152 if (pos > last_pos) {
1153 out += str->substr(last_pos, pos - last_pos);
1154 }
1155
1156 // try to parse the variable name into var, either \$\{(.+)\} or
1157 // \$[a-z\_]+
1158 const char *valid_chars = "abcdefghijklmnopqrstuvwxyz_";
1159 string var;
1160 size_t endpos = 0;
1161 if ((*str)[pos+1] == '{') {
1162 // ...${foo_bar}...
1163 endpos = str->find_first_not_of(valid_chars, pos + 2);
1164 if (endpos != std::string::npos &&
1165 (*str)[endpos] == '}') {
1166 var = str->substr(pos + 2, endpos - pos - 2);
1167 endpos++;
1168 }
7c673cae 1169 } else {
11fdf7f2
TL
1170 // ...$foo...
1171 endpos = str->find_first_not_of(valid_chars, pos + 1);
1172 if (endpos != std::string::npos)
1173 var = str->substr(pos + 1, endpos - pos - 1);
1174 else
1175 var = str->substr(pos + 1);
7c673cae 1176 }
11fdf7f2
TL
1177 last_pos = endpos;
1178
1179 if (!var.size()) {
1180 out += '$';
1181 } else {
1182 //cout << " found var " << var << std::endl;
1183 // special metavariable?
1184 if (var == "type") {
1185 out += values.name.get_type_name();
1186 } else if (var == "cluster") {
1187 out += values.cluster;
1188 } else if (var == "name") {
1189 out += values.name.to_cstr();
1190 } else if (var == "host") {
1191 if (values.host == "") {
1192 out += ceph_get_short_hostname();
1193 } else {
1194 out += values.host;
1195 }
1196 } else if (var == "num") {
1197 out += values.name.get_id().c_str();
1198 } else if (var == "id") {
1199 out += values.name.get_id();
1200 } else if (var == "pid") {
1201 out += stringify(getpid());
1202 if (o) {
1203 may_reexpand_meta[o->name] = *str;
1204 }
1205 } else if (var == "cctid") {
1206 out += stringify((unsigned long long)this);
1207 } else if (var == "home") {
1208 const char *home = getenv("HOME");
1209 out = home ? std::string(home) : std::string();
1210 } else {
1211 if (var == "data_dir") {
1212 var = data_dir_option;
1213 }
1214 const Option *o = find_option(var);
1215 if (!o) {
1216 out += str->substr(pos, endpos - pos);
1217 } else {
1218 auto match = std::find_if(
1219 stack->begin(), stack->end(),
1220 [o](pair<const Option *,const Option::value_t*>& item) {
1221 return item.first == o;
1222 });
1223 if (match != stack->end()) {
1224 // substitution loop; break the cycle
1225 if (err) {
1226 *err << "variable expansion loop at " << var << "="
1227 << Option::to_str(*match->second) << "\n"
1228 << "expansion stack:\n";
1229 for (auto i = stack->rbegin(); i != stack->rend(); ++i) {
1230 *err << i->first->name << "="
1231 << Option::to_str(*i->second) << "\n";
1232 }
1233 }
1234 return Option::value_t(std::string("$") + o->name);
1235 } else {
1236 // recursively evaluate!
1237 string n;
1238 conf_stringify(_get_val(values, *o, stack, err), &n);
1239 out += n;
1240 }
1241 }
1242 }
1243 }
1244 pos = str->find('$', last_pos);
7c673cae 1245 }
11fdf7f2
TL
1246 if (last_pos != std::string::npos) {
1247 out += str->substr(last_pos);
1248 }
1249 if (o) {
1250 stack->pop_back();
1251 }
1252
1253 return Option::value_t(out);
7c673cae
FG
1254}
1255
11fdf7f2
TL
1256int md_config_t::_get_val_cstr(
1257 const ConfigValues& values,
9f95a23c 1258 const std::string& key, char **buf, int len) const
7c673cae 1259{
c07f9fc5 1260 if (key.empty())
7c673cae
FG
1261 return -EINVAL;
1262
11fdf7f2
TL
1263 string val;
1264 if (conf_stringify(_get_val(values, key), &val) == 0) {
224ce89b 1265 int l = val.length() + 1;
7c673cae
FG
1266 if (len == -1) {
1267 *buf = (char*)malloc(l);
1268 if (!*buf)
1269 return -ENOMEM;
224ce89b 1270 strncpy(*buf, val.c_str(), l);
7c673cae
FG
1271 return 0;
1272 }
224ce89b 1273 snprintf(*buf, len, "%s", val.c_str());
7c673cae
FG
1274 return (l > len) ? -ENAMETOOLONG : 0;
1275 }
1276
7c673cae
FG
1277 // couldn't find a configuration option with key 'k'
1278 return -ENOENT;
1279}
1280
1281void md_config_t::get_all_keys(std::vector<std::string> *keys) const {
1282 const std::string negative_flag_prefix("no_");
1283
1284 keys->clear();
c07f9fc5
FG
1285 keys->reserve(schema.size());
1286 for (const auto &i: schema) {
1287 const Option &opt = i.second;
7c673cae 1288 keys->push_back(opt.name);
c07f9fc5 1289 if (opt.type == Option::TYPE_BOOL) {
7c673cae
FG
1290 keys->push_back(negative_flag_prefix + opt.name);
1291 }
1292 }
7c673cae
FG
1293}
1294
1295/* The order of the sections here is important. The first section in the
1296 * vector is the "highest priority" section; if we find it there, we'll stop
1297 * looking. The lowest priority section is the one we look in only if all
1298 * others had nothing. This should always be the global section.
1299 */
11fdf7f2
TL
1300void md_config_t::get_my_sections(const ConfigValues& values,
1301 std::vector <std::string> &sections) const
7c673cae 1302{
11fdf7f2 1303 _get_my_sections(values, sections);
7c673cae
FG
1304}
1305
11fdf7f2
TL
1306void md_config_t::_get_my_sections(const ConfigValues& values,
1307 std::vector <std::string> &sections) const
7c673cae 1308{
11fdf7f2 1309 sections.push_back(values.name.to_str());
7c673cae 1310
11fdf7f2 1311 sections.push_back(values.name.get_type_name());
7c673cae
FG
1312
1313 sections.push_back("global");
1314}
1315
1316// Return a list of all sections
1317int md_config_t::get_all_sections(std::vector <std::string> &sections) const
1318{
9f95a23c
TL
1319 for (auto [section_name, section] : cf) {
1320 sections.push_back(section_name);
1321 std::ignore = section;
7c673cae
FG
1322 }
1323 return 0;
1324}
1325
11fdf7f2
TL
1326int md_config_t::get_val_from_conf_file(
1327 const ConfigValues& values,
1328 const std::vector <std::string> &sections,
9f95a23c 1329 const std::string_view key,
11fdf7f2
TL
1330 std::string &out,
1331 bool emeta) const
7c673cae 1332{
11fdf7f2
TL
1333 int r = _get_val_from_conf_file(sections, key, out);
1334 if (r < 0) {
1335 return r;
1336 }
1337 if (emeta) {
1338 expand_stack_t stack;
1339 auto v = _expand_meta(values, Option::value_t(out), nullptr, &stack, nullptr);
1340 conf_stringify(v, &out);
1341 }
1342 return 0;
7c673cae
FG
1343}
1344
11fdf7f2
TL
1345int md_config_t::_get_val_from_conf_file(
1346 const std::vector <std::string> &sections,
9f95a23c 1347 const std::string_view key,
11fdf7f2 1348 std::string &out) const
7c673cae 1349{
9f95a23c
TL
1350 for (auto &s : sections) {
1351 int ret = cf.read(s.c_str(), std::string{key}, out);
7c673cae 1352 if (ret == 0) {
7c673cae 1353 return 0;
11fdf7f2 1354 } else if (ret != -ENOENT) {
7c673cae 1355 return ret;
11fdf7f2 1356 }
7c673cae
FG
1357 }
1358 return -ENOENT;
1359}
1360
11fdf7f2
TL
1361int md_config_t::_set_val(
1362 ConfigValues& values,
1363 const ConfigTracker& observers,
1364 const std::string &raw_val,
1365 const Option &opt,
1366 int level,
1367 std::string *error_message)
7c673cae 1368{
11fdf7f2 1369 Option::value_t new_value;
81eedcae 1370 ceph_assert(error_message);
11fdf7f2
TL
1371 int r = opt.parse_value(raw_val, &new_value, error_message);
1372 if (r < 0) {
c07f9fc5
FG
1373 return r;
1374 }
7c673cae 1375
11fdf7f2
TL
1376 // unsafe runtime change?
1377 if (!opt.can_update_at_runtime() &&
1378 safe_to_start_threads &&
1379 !observers.is_tracking(opt.name)) {
1380 // accept value if it is not actually a change
1381 if (new_value != _get_val_nometa(values, opt)) {
1382 *error_message = string("Configuration option '") + opt.name +
1383 "' may not be modified at runtime";
9f95a23c 1384 return -EPERM;
c07f9fc5 1385 }
7c673cae 1386 }
7c673cae 1387
c07f9fc5 1388 // Apply the value to its entry in the `values` map
11fdf7f2
TL
1389 auto result = values.set_value(opt.name, std::move(new_value), level);
1390 switch (result) {
1391 case ConfigValues::SET_NO_CHANGE:
1392 break;
1393 case ConfigValues::SET_NO_EFFECT:
1394 values_bl.clear();
1395 break;
1396 case ConfigValues::SET_HAVE_EFFECT:
1397 values_bl.clear();
1398 _refresh(values, opt);
1399 break;
1400 }
1401 return result;
1402}
7c673cae 1403
11fdf7f2
TL
1404void md_config_t::_refresh(ConfigValues& values, const Option& opt)
1405{
c07f9fc5
FG
1406 // Apply the value to its legacy field, if it has one
1407 auto legacy_ptr_iter = legacy_values.find(std::string(opt.name));
1408 if (legacy_ptr_iter != legacy_values.end()) {
11fdf7f2
TL
1409 update_legacy_val(values, opt, legacy_ptr_iter->second);
1410 }
1411 // Was this a debug_* option update?
1412 if (opt.subsys >= 0) {
1413 string actual_val;
1414 conf_stringify(_get_val(values, opt), &actual_val);
1415 values.set_logging(opt.subsys, actual_val.c_str());
1416 } else {
1417 // normal option, advertise the change.
1418 values.changed.insert(opt.name);
7c673cae 1419 }
11fdf7f2 1420}
7c673cae 1421
11fdf7f2 1422int md_config_t::_rm_val(ConfigValues& values,
9f95a23c 1423 const std::string_view key,
11fdf7f2
TL
1424 int level)
1425{
1426 if (schema.count(key) == 0) {
1427 return -EINVAL;
1428 }
9f95a23c 1429 auto ret = values.rm_val(std::string{key}, level);
11fdf7f2
TL
1430 if (ret < 0) {
1431 return ret;
1432 }
1433 if (ret == ConfigValues::SET_HAVE_EFFECT) {
1434 _refresh(values, *find_option(key));
1435 }
1436 values_bl.clear();
c07f9fc5
FG
1437 return 0;
1438}
7c673cae 1439
11fdf7f2
TL
1440namespace {
1441template<typename Size>
1442struct get_size_visitor : public boost::static_visitor<Size>
1443{
1444 get_size_visitor() {}
1445
1446 template<typename T>
1447 Size operator()(const T&) const {
1448 return -1;
1449 }
1450 Size operator()(const Option::size_t& sz) const {
1451 return static_cast<Size>(sz.value);
1452 }
1453 Size operator()(const Size& v) const {
1454 return v;
1455 }
1456};
1457
c07f9fc5
FG
1458/**
1459 * Handles assigning from a variant-of-types to a variant-of-pointers-to-types
1460 */
11fdf7f2 1461template<class Config>
c07f9fc5
FG
1462class assign_visitor : public boost::static_visitor<>
1463{
11fdf7f2 1464 Config *conf;
c07f9fc5
FG
1465 Option::value_t val;
1466 public:
7c673cae 1467
11fdf7f2 1468 assign_visitor(Config *conf_, Option::value_t val_)
c07f9fc5
FG
1469 : conf(conf_), val(val_)
1470 {}
1471
1472 template <typename T>
11fdf7f2 1473 void operator()(T Config::* ptr) const
c07f9fc5 1474 {
11fdf7f2 1475 T *member = const_cast<T *>(&(conf->*(boost::get<const T Config::*>(ptr))));
c07f9fc5
FG
1476
1477 *member = boost::get<T>(val);
7c673cae 1478 }
11fdf7f2
TL
1479 void operator()(uint64_t Config::* ptr) const
1480 {
1481 using T = uint64_t;
1482 auto member = const_cast<T*>(&(conf->*(boost::get<const T Config::*>(ptr))));
1483 *member = boost::apply_visitor(get_size_visitor<T>{}, val);
1484 }
1485 void operator()(int64_t Config::* ptr) const
1486 {
1487 using T = int64_t;
1488 auto member = const_cast<T*>(&(conf->*(boost::get<const T Config::*>(ptr))));
1489 *member = boost::apply_visitor(get_size_visitor<T>{}, val);
c07f9fc5 1490 }
7c673cae 1491};
11fdf7f2 1492} // anonymous namespace
7c673cae 1493
11fdf7f2 1494void md_config_t::update_legacy_vals(ConfigValues& values)
7c673cae 1495{
11fdf7f2
TL
1496 for (const auto &i : legacy_values) {
1497 const auto &name = i.first;
1498 const auto &option = schema.at(name);
1499 auto ptr = i.second;
1500 update_legacy_val(values, option, ptr);
7c673cae 1501 }
7c673cae
FG
1502}
1503
11fdf7f2
TL
1504void md_config_t::update_legacy_val(ConfigValues& values,
1505 const Option &opt,
1506 md_config_t::member_ptr_t member_ptr)
7c673cae 1507{
11fdf7f2
TL
1508 Option::value_t v = _get_val(values, opt);
1509 boost::apply_visitor(assign_visitor(&values, v), member_ptr);
7c673cae
FG
1510}
1511
11fdf7f2 1512static void dump(Formatter *f, int level, Option::value_t in)
7c673cae 1513{
11fdf7f2
TL
1514 if (const bool *v = boost::get<const bool>(&in)) {
1515 f->dump_bool(ceph_conf_level_name(level), *v);
1516 } else if (const int64_t *v = boost::get<const int64_t>(&in)) {
1517 f->dump_int(ceph_conf_level_name(level), *v);
1518 } else if (const uint64_t *v = boost::get<const uint64_t>(&in)) {
1519 f->dump_unsigned(ceph_conf_level_name(level), *v);
1520 } else if (const double *v = boost::get<const double>(&in)) {
1521 f->dump_float(ceph_conf_level_name(level), *v);
1522 } else {
1523 f->dump_stream(ceph_conf_level_name(level)) << Option::to_str(in);
7c673cae 1524 }
7c673cae
FG
1525}
1526
1527void md_config_t::diff(
11fdf7f2
TL
1528 const ConfigValues& values,
1529 Formatter *f,
1530 string name) const
7c673cae 1531{
11fdf7f2 1532 values.for_each([this, f, &values] (auto& name, auto& configs) {
494da23a 1533 if (configs.empty()) {
11fdf7f2 1534 return;
7c673cae 1535 }
9f95a23c 1536 f->open_object_section(std::string{name}.c_str());
11fdf7f2 1537 const Option *o = find_option(name);
494da23a
TL
1538 if (configs.size() &&
1539 configs.begin()->first != CONF_DEFAULT) {
1540 // show compiled-in default only if an override default wasn't provided
1541 dump(f, CONF_DEFAULT, _get_val_default(*o));
1542 }
11fdf7f2
TL
1543 for (auto& j : configs) {
1544 dump(f, j.first, j.second);
31f18b77 1545 }
11fdf7f2
TL
1546 dump(f, CONF_FINAL, _get_val(values, *o));
1547 f->close_section();
1548 });
7c673cae
FG
1549}
1550
9f95a23c 1551void md_config_t::complain_about_parse_error(CephContext *cct)
7c673cae 1552{
9f95a23c 1553 ::complain_about_parse_error(cct, parse_error);
7c673cae 1554}