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