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