Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / common / config.cc
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 "common/ceph_argparse.h"
16 #include "common/common_init.h"
17 #include "common/config.h"
18 #include "include/str_list.h"
19 #include "include/stringify.h"
20 #include "osd/osd_types.h"
21 #include "common/errno.h"
22 #include "common/hostname.h"
23
24 #include <boost/type_traits.hpp>
25
26 /* Don't use standard Ceph logging in this file.
27  * We can't use logging until it's initialized, and a lot of the necessary
28  * initialization happens here.
29  */
30 #undef dout
31 #undef ldout
32 #undef pdout
33 #undef derr
34 #undef lderr
35 #undef generic_dout
36 #undef dendl
37
38 using std::map;
39 using std::list;
40 using std::ostringstream;
41 using std::pair;
42 using std::string;
43
44 const char *CEPH_CONF_FILE_DEFAULT = "$data_dir/config, /etc/ceph/$cluster.conf, ~/.ceph/$cluster.conf, $cluster.conf"
45 #if defined(__FreeBSD__)
46     ", /usr/local/etc/ceph/$cluster.conf"
47 #endif
48     ;
49
50 #define _STR(x) #x
51 #define STRINGIFY(x) _STR(x)
52
53 int ceph_resolve_file_search(const std::string& filename_list,
54                              std::string& result)
55 {
56   list<string> ls;
57   get_str_list(filename_list, ls);
58
59   int ret = -ENOENT;
60   list<string>::iterator iter;
61   for (iter = ls.begin(); iter != ls.end(); ++iter) {
62     int fd = ::open(iter->c_str(), O_RDONLY);
63     if (fd < 0) {
64       ret = -errno;
65       continue;
66     }
67     close(fd);
68     result = *iter;
69     return 0;
70   }
71
72   return ret;
73 }
74
75
76
77 md_config_t::md_config_t(bool is_daemon)
78   : cluster(""),
79   lock("md_config_t", true, false)
80 {
81   init_subsys();
82
83   // Load the compile-time list of Option into
84   // a map so that we can resolve keys quickly.
85   for (const auto &i : ceph_options) {
86     if (schema.count(i.name)) {
87       // We may be instantiated pre-logging so send 
88       std::cerr << "Duplicate config key in schema: '" << i.name << "'"
89                 << std::endl;
90       assert(false);
91     }
92     schema.insert({i.name, i});
93   }
94
95   // Populate list of legacy_values according to the OPTION() definitions
96   // Note that this is just setting up our map of name->member ptr.  The
97   // default values etc will get loaded in along with new-style data,
98   // as all loads write to both the values map, and the legacy
99   // members if present.
100   legacy_values = {
101 #define OPTION(name, type) \
102     {std::string(STRINGIFY(name)), &md_config_t::name},
103 #define SAFE_OPTION(name, type) OPTION(name, type)
104 #include "common/legacy_config_opts.h"
105 #undef OPTION
106 #undef SAFE_OPTION
107   };
108
109   validate_schema();
110
111   // Load default values from the schema
112   for (const auto &i : schema) {
113     const Option &opt = i.second;
114     bool has_daemon_default = !boost::get<boost::blank>(&opt.daemon_value);
115     Option::value_t default_val;
116     if (is_daemon && has_daemon_default) {
117       default_val = opt.daemon_value;
118     } else {
119       default_val = opt.value;
120     }
121
122     if (opt.type == Option::TYPE_STR) {
123       // We call pre_validate as a sanity check, but also to get any
124       // side effect (value modification) from the validator.
125       std::string *def_str = boost::get<std::string>(&default_val);
126       std::string err;
127       if (opt.pre_validate(def_str, &err) != 0) {
128         std::cerr << "Default value " << opt.name << "=" << *def_str << " is "
129                      "invalid: " << err << std::endl;
130
131         // This is the compiled-in default that is failing its own option's
132         // validation, so this is super-invalid and should never make it
133         // past a pull request: crash out.
134         assert(false);
135       }
136     }
137
138     values[i.first] = default_val;
139   }
140
141   // Copy out values (defaults) into any legacy (C struct member) fields
142   for (const auto &i : legacy_values) {
143     const auto &name = i.first;
144     const auto &option = schema.at(name);
145     auto ptr = i.second;
146
147     update_legacy_val(option, ptr);
148   }
149 }
150
151 /**
152  * Sanity check schema.  Assert out on failures, to ensure any bad changes
153  * cannot possibly pass any testing and make it into a release.
154  */
155 void md_config_t::validate_schema()
156 {
157   for (const auto &i : schema) {
158     const auto &opt = i.second;
159     for (const auto &see_also_key : opt.see_also) {
160       if (schema.count(see_also_key) == 0) {
161         std::cerr << "Non-existent see-also key '" << see_also_key
162                   << "' on option '" << opt.name << "'" << std::endl;
163         assert(false);
164       }
165     }
166   }
167
168   for (const auto &i : legacy_values) {
169     if (schema.count(i.first) == 0) {
170       std::cerr << "Schema is missing legacy field '" << i.first << "'"
171                 << std::endl;
172       assert(false);
173     }
174   }
175 }
176
177 void md_config_t::init_subsys()
178 {
179 #define SUBSYS(name, log, gather) \
180   subsys.add(ceph_subsys_##name, STRINGIFY(name), log, gather);
181 #define DEFAULT_SUBSYS(log, gather) \
182   subsys.add(ceph_subsys_, "none", log, gather);
183 #include "common/subsys.h"
184 #undef SUBSYS
185 #undef DEFAULT_SUBSYS
186 }
187
188 md_config_t::~md_config_t()
189 {
190 }
191
192 void md_config_t::add_observer(md_config_obs_t* observer_)
193 {
194   Mutex::Locker l(lock);
195   const char **keys = observer_->get_tracked_conf_keys();
196   for (const char ** k = keys; *k; ++k) {
197     obs_map_t::value_type val(*k, observer_);
198     observers.insert(val);
199   }
200 }
201
202 void md_config_t::remove_observer(md_config_obs_t* observer_)
203 {
204   Mutex::Locker l(lock);
205   bool found_obs = false;
206   for (obs_map_t::iterator o = observers.begin(); o != observers.end(); ) {
207     if (o->second == observer_) {
208       observers.erase(o++);
209       found_obs = true;
210     }
211     else {
212       ++o;
213     }
214   }
215   assert(found_obs);
216 }
217
218 int md_config_t::parse_config_files(const char *conf_files,
219                                     std::ostream *warnings,
220                                     int flags)
221 {
222   Mutex::Locker l(lock);
223
224   if (internal_safe_to_start_threads)
225     return -ENOSYS;
226
227   if (!cluster.size() && !conf_files) {
228     /*
229      * set the cluster name to 'ceph' when neither cluster name nor
230      * configuration file are specified.
231      */
232     cluster = "ceph";
233   }
234
235   if (!conf_files) {
236     const char *c = getenv("CEPH_CONF");
237     if (c) {
238       conf_files = c;
239     }
240     else {
241       if (flags & CINIT_FLAG_NO_DEFAULT_CONFIG_FILE)
242         return 0;
243       conf_files = CEPH_CONF_FILE_DEFAULT;
244     }
245   }
246
247   std::list<std::string> cfl;
248   get_str_list(conf_files, cfl);
249
250   auto p = cfl.begin();
251   while (p != cfl.end()) {
252     // expand $data_dir?
253     string &s = *p;
254     if (s.find("$data_dir") != string::npos) {
255       if (data_dir_option.length()) {
256         list<const Option *> stack;
257         expand_meta(s, NULL, stack, warnings);
258         p++;
259       } else {
260         cfl.erase(p++);  // ignore this item
261       }
262     } else {
263       ++p;
264     }
265   }
266   return parse_config_files_impl(cfl, warnings);
267 }
268
269 int md_config_t::parse_config_files_impl(const std::list<std::string> &conf_files,
270                                          std::ostream *warnings)
271 {
272   assert(lock.is_locked());
273
274   // open new conf
275   list<string>::const_iterator c;
276   for (c = conf_files.begin(); c != conf_files.end(); ++c) {
277     cf.clear();
278     string fn = *c;
279     expand_meta(fn, warnings);
280     int ret = cf.parse_file(fn.c_str(), &parse_errors, warnings);
281     if (ret == 0)
282       break;
283     else if (ret != -ENOENT)
284       return ret;
285   }
286   // it must have been all ENOENTs, that's the only way we got here
287   if (c == conf_files.end())
288     return -ENOENT;
289
290   if (cluster.size() == 0) {
291     /*
292      * If cluster name is not set yet, use the prefix of the
293      * basename of configuration file as cluster name.
294      */
295     auto start = c->rfind('/') + 1;
296     auto end = c->find(".conf", start);
297     if (end == c->npos) {
298         /*
299          * If the configuration file does not follow $cluster.conf
300          * convention, we do the last try and assign the cluster to
301          * 'ceph'.
302          */
303         cluster = "ceph";
304     } else {
305       cluster = c->substr(start, end - start);
306     }
307   }
308
309   std::vector <std::string> my_sections;
310   _get_my_sections(my_sections);
311   for (const auto &i : schema) {
312     const auto &opt = i.second;
313     std::string val;
314     int ret = _get_val_from_conf_file(my_sections, opt.name, val, false);
315     if (ret == 0) {
316       std::string error_message;
317       int r = set_val_impl(val, opt, &error_message);
318       if (warnings != nullptr && (r != 0 || !error_message.empty())) {
319         *warnings << "parse error setting '" << opt.name << "' to '" << val
320                   << "'";
321         if (!error_message.empty()) {
322           *warnings << " (" << error_message << ")";
323         }
324         *warnings << std::endl;
325       }
326     }
327   }
328
329   // subsystems?
330   for (size_t o = 0; o < subsys.get_num(); o++) {
331     std::string as_option("debug_");
332     as_option += subsys.get_name(o);
333     std::string val;
334     int ret = _get_val_from_conf_file(my_sections, as_option.c_str(), val, false);
335     if (ret == 0) {
336       int log, gather;
337       int r = sscanf(val.c_str(), "%d/%d", &log, &gather);
338       if (r >= 1) {
339         if (r < 2)
340           gather = log;
341         //      cout << "config subsys " << subsys.get_name(o) << " log " << log << " gather " << gather << std::endl;
342         subsys.set_log_level(o, log);
343         subsys.set_gather_level(o, gather);
344       }
345     }   
346   }
347
348   // Warn about section names that look like old-style section names
349   std::deque < std::string > old_style_section_names;
350   for (ConfFile::const_section_iter_t s = cf.sections_begin();
351        s != cf.sections_end(); ++s) {
352     const string &str(s->first);
353     if (((str.find("mds") == 0) || (str.find("mon") == 0) ||
354          (str.find("osd") == 0)) && (str.size() > 3) && (str[3] != '.')) {
355       old_style_section_names.push_back(str);
356     }
357   }
358   if (!old_style_section_names.empty()) {
359     ostringstream oss;
360     cerr << "ERROR! old-style section name(s) found: ";
361     string sep;
362     for (std::deque < std::string >::const_iterator os = old_style_section_names.begin();
363          os != old_style_section_names.end(); ++os) {
364       cerr << sep << *os;
365       sep = ", ";
366     }
367     cerr << ". Please use the new style section names that include a period.";
368   }
369   return 0;
370 }
371
372 void md_config_t::parse_env()
373 {
374   Mutex::Locker l(lock);
375   if (internal_safe_to_start_threads)
376     return;
377   if (getenv("CEPH_KEYRING")) {
378     set_val_or_die("keyring", getenv("CEPH_KEYRING"));
379   }
380 }
381
382 void md_config_t::show_config(std::ostream& out)
383 {
384   Mutex::Locker l(lock);
385   _show_config(&out, NULL);
386 }
387
388 void md_config_t::show_config(Formatter *f)
389 {
390   Mutex::Locker l(lock);
391   _show_config(NULL, f);
392 }
393
394 void md_config_t::_show_config(std::ostream *out, Formatter *f)
395 {
396   if (out) {
397     *out << "name = " << name << std::endl;
398     *out << "cluster = " << cluster << std::endl;
399   }
400   if (f) {
401     f->dump_string("name", stringify(name));
402     f->dump_string("cluster", cluster);
403   }
404   for (size_t o = 0; o < subsys.get_num(); o++) {
405     if (out)
406       *out << "debug_" << subsys.get_name(o)
407            << " = " << subsys.get_log_level(o)
408            << "/" << subsys.get_gather_level(o) << std::endl;
409     if (f) {
410       ostringstream ss;
411       std::string debug_name = "debug_";
412       debug_name += subsys.get_name(o);
413       ss << subsys.get_log_level(o)
414          << "/" << subsys.get_gather_level(o);
415       f->dump_string(debug_name.c_str(), ss.str());
416     }
417   }
418   for (const auto& i: schema) {
419     const Option &opt = i.second;
420     char *buf;
421     _get_val(opt.name, &buf, -1);
422     if (out)
423       *out << opt.name << " = " << buf << std::endl;
424     if (f)
425       f->dump_string(opt.name.c_str(), buf);
426     free(buf);
427   }
428 }
429
430 int md_config_t::parse_argv(std::vector<const char*>& args)
431 {
432   Mutex::Locker l(lock);
433   if (internal_safe_to_start_threads) {
434     return -ENOSYS;
435   }
436
437   bool show_config = false;
438   bool show_config_value = false;
439   string show_config_value_arg;
440
441   // In this function, don't change any parts of the configuration directly.
442   // Instead, use set_val to set them. This will allow us to send the proper
443   // observer notifications later.
444   std::string val;
445   for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
446     if (strcmp(*i, "--") == 0) {
447       /* Normally we would use ceph_argparse_double_dash. However, in this
448        * function we *don't* want to remove the double dash, because later
449        * argument parses will still need to see it. */
450       break;
451     }
452     else if (ceph_argparse_flag(args, i, "--show_conf", (char*)NULL)) {
453       cerr << cf << std::endl;
454       _exit(0);
455     }
456     else if (ceph_argparse_flag(args, i, "--show_config", (char*)NULL)) {
457       show_config = true;
458     }
459     else if (ceph_argparse_witharg(args, i, &val, "--show_config_value", (char*)NULL)) {
460       show_config_value = true;
461       show_config_value_arg = val;
462     }
463     else if (ceph_argparse_flag(args, i, "--foreground", "-f", (char*)NULL)) {
464       set_val_or_die("daemonize", "false");
465     }
466     else if (ceph_argparse_flag(args, i, "-d", (char*)NULL)) {
467       set_val_or_die("daemonize", "false");
468       set_val_or_die("log_file", "");
469       set_val_or_die("log_to_stderr", "true");
470       set_val_or_die("err_to_stderr", "true");
471       set_val_or_die("log_to_syslog", "false");
472     }
473     // Some stuff that we wanted to give universal single-character options for
474     // Careful: you can burn through the alphabet pretty quickly by adding
475     // to this list.
476     else if (ceph_argparse_witharg(args, i, &val, "--monmap", "-M", (char*)NULL)) {
477       set_val_or_die("monmap", val.c_str());
478     }
479     else if (ceph_argparse_witharg(args, i, &val, "--mon_host", "-m", (char*)NULL)) {
480       set_val_or_die("mon_host", val.c_str());
481     }
482     else if (ceph_argparse_witharg(args, i, &val, "--bind", (char*)NULL)) {
483       set_val_or_die("public_addr", val.c_str());
484     }
485     else if (ceph_argparse_witharg(args, i, &val, "--keyfile", "-K", (char*)NULL)) {
486       set_val_or_die("keyfile", val.c_str());
487     }
488     else if (ceph_argparse_witharg(args, i, &val, "--keyring", "-k", (char*)NULL)) {
489       set_val_or_die("keyring", val.c_str());
490     }
491     else if (ceph_argparse_witharg(args, i, &val, "--client_mountpoint", "-r", (char*)NULL)) {
492       set_val_or_die("client_mountpoint", val.c_str());
493     }
494     else {
495       int r = parse_option(args, i, NULL);
496       if (r < 0) {
497         return r;
498       }
499     }
500   }
501
502   if (show_config) {
503     expand_all_meta();
504     _show_config(&cout, NULL);
505     _exit(0);
506   }
507
508   if (show_config_value) {
509     char *buf = 0;
510     int r = _get_val(show_config_value_arg.c_str(), &buf, -1);
511     if (r < 0) {
512       if (r == -ENOENT)
513         std::cerr << "failed to get config option '" <<
514           show_config_value_arg << "': option not found" << std::endl;
515       else
516         std::cerr << "failed to get config option '" <<
517           show_config_value_arg << "': " << cpp_strerror(r) << std::endl;
518       _exit(1);
519     }
520     string s = buf;
521     expand_meta(s, &std::cerr);
522     std::cout << s << std::endl;
523     _exit(0);
524   }
525
526   return 0;
527 }
528
529 int md_config_t::parse_option(std::vector<const char*>& args,
530                                std::vector<const char*>::iterator& i,
531                                ostream *oss)
532 {
533   int ret = 0;
534   size_t o = 0;
535   std::string val;
536
537   // subsystems?
538   for (o = 0; o < subsys.get_num(); o++) {
539     std::string as_option("--");
540     as_option += "debug_";
541     as_option += subsys.get_name(o);
542     ostringstream err;
543     if (ceph_argparse_witharg(args, i, &val, err,
544                               as_option.c_str(), (char*)NULL)) {
545       if (err.tellp()) {
546         if (oss) {
547           *oss << err.str();
548         }
549         ret = -EINVAL;
550         break;
551       }
552       int log, gather;
553       int r = sscanf(val.c_str(), "%d/%d", &log, &gather);
554       if (r >= 1) {
555         if (r < 2)
556           gather = log;
557         //        cout << "subsys " << subsys.get_name(o) << " log " << log << " gather " << gather << std::endl;
558         subsys.set_log_level(o, log);
559         subsys.set_gather_level(o, gather);
560         if (oss)
561           *oss << "debug_" << subsys.get_name(o) << "=" << log << "/" << gather << " ";
562       }
563       break;
564     }   
565   }
566   if (o < subsys.get_num()) {
567     return ret;
568   }
569
570   std::string option_name;
571   std::string error_message;
572   o = 0;
573   for (const auto& opt_iter: schema) {
574     const Option &opt = opt_iter.second;
575     ostringstream err;
576     std::string as_option("--");
577     as_option += opt.name;
578     option_name = opt.name;
579     if (opt.type == Option::TYPE_BOOL) {
580       int res;
581       if (ceph_argparse_binary_flag(args, i, &res, oss, as_option.c_str(),
582                                     (char*)NULL)) {
583         if (res == 0)
584           ret = set_val_impl("false", opt, &error_message);
585         else if (res == 1)
586           ret = set_val_impl("true", opt, &error_message);
587         else
588           ret = res;
589         break;
590       } else {
591         std::string no("--no-");
592         no += opt.name;
593         if (ceph_argparse_flag(args, i, no.c_str(), (char*)NULL)) {
594           ret = set_val_impl("false", opt, &error_message);
595           break;
596         }
597       }
598     } else if (ceph_argparse_witharg(args, i, &val, err,
599                                      as_option.c_str(), (char*)NULL)) {
600       if (!err.str().empty()) {
601         error_message = err.str();
602         ret = -EINVAL;
603         break;
604       }
605       if (oss && ((!opt.is_safe()) &&
606                   (observers.find(opt.name) == observers.end()))) {
607         *oss << "You cannot change " << opt.name << " using injectargs.\n";
608         return -ENOSYS;
609       }
610       ret = set_val_impl(val, opt, &error_message);
611       break;
612     }
613     ++o;
614   }
615
616   if (ret != 0 || !error_message.empty()) {
617     assert(!option_name.empty());
618     if (oss) {
619       *oss << "Parse error setting " << option_name << " to '"
620            << val << "' using injectargs";
621       if (!error_message.empty()) {
622         *oss << " (" << error_message << ")";
623       }
624       *oss << ".\n";
625     } else {
626       cerr << "parse error setting '" << option_name << "' to '"
627            << val << "'";
628       if (!error_message.empty()) {
629         cerr << " (" << error_message << ")";
630       }
631       cerr << "\n" << std::endl;
632     }
633   }
634
635   if (o == schema.size()) {
636     // ignore
637     ++i;
638   }
639   return ret;
640 }
641
642 int md_config_t::parse_injectargs(std::vector<const char*>& args,
643                                   std::ostream *oss)
644 {
645   assert(lock.is_locked());
646   int ret = 0;
647   for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
648     int r = parse_option(args, i, oss);
649     if (r < 0)
650       ret = r;
651   }
652   return ret;
653 }
654
655 void md_config_t::apply_changes(std::ostream *oss)
656 {
657   Mutex::Locker l(lock);
658   /*
659    * apply changes until the cluster name is assigned
660    */
661   if (cluster.size())
662     _apply_changes(oss);
663 }
664
665 bool md_config_t::_internal_field(const string& s)
666 {
667   if (s == "internal_safe_to_start_threads")
668     return true;
669   return false;
670 }
671
672 void md_config_t::_apply_changes(std::ostream *oss)
673 {
674   /* Maps observers to the configuration options that they care about which
675    * have changed. */
676   typedef std::map < md_config_obs_t*, std::set <std::string> > rev_obs_map_t;
677
678   expand_all_meta();
679
680   // expand_all_meta could have modified anything.  Copy it all out again.
681   for (const auto &i : legacy_values) {
682     const auto &name = i.first;
683     const auto &option = schema.at(name);
684     auto ptr = i.second;
685
686     update_legacy_val(option, ptr);
687   }
688
689   // create the reverse observer mapping, mapping observers to the set of
690   // changed keys that they'll get.
691   rev_obs_map_t robs;
692   std::set <std::string> empty_set;
693   char buf[128];
694   char *bufptr = (char*)buf;
695   for (changed_set_t::const_iterator c = changed.begin();
696        c != changed.end(); ++c) {
697     const std::string &key(*c);
698     pair < obs_map_t::iterator, obs_map_t::iterator >
699       range(observers.equal_range(key));
700     if ((oss) &&
701         (!_get_val(key.c_str(), &bufptr, sizeof(buf))) &&
702         !_internal_field(key)) {
703       (*oss) << key << " = '" << buf << "' ";
704       if (range.first == range.second) {
705         (*oss) << "(not observed, change may require restart) ";
706       }
707     }
708     for (obs_map_t::iterator r = range.first; r != range.second; ++r) {
709       rev_obs_map_t::value_type robs_val(r->second, empty_set);
710       pair < rev_obs_map_t::iterator, bool > robs_ret(robs.insert(robs_val));
711       std::set <std::string> &keys(robs_ret.first->second);
712       keys.insert(key);
713     }
714   }
715
716   changed.clear();
717
718   // Make any pending observer callbacks
719   for (rev_obs_map_t::const_iterator r = robs.begin(); r != robs.end(); ++r) {
720     md_config_obs_t *obs = r->first;
721     obs->handle_conf_change(this, r->second);
722   }
723
724 }
725
726 void md_config_t::call_all_observers()
727 {
728   std::map<md_config_obs_t*,std::set<std::string> > obs;
729   {
730     Mutex::Locker l(lock);
731
732     expand_all_meta();
733
734     for (auto r = observers.begin(); r != observers.end(); ++r) {
735       obs[r->second].insert(r->first);
736     }
737   }
738   for (auto p = obs.begin();
739        p != obs.end();
740        ++p) {
741     p->first->handle_conf_change(this, p->second);
742   }
743 }
744
745 int md_config_t::injectargs(const std::string& s, std::ostream *oss)
746 {
747   int ret;
748   Mutex::Locker l(lock);
749   char b[s.length()+1];
750   strcpy(b, s.c_str());
751   std::vector<const char*> nargs;
752   char *p = b;
753   while (*p) {
754     nargs.push_back(p);
755     while (*p && *p != ' ') p++;
756     if (!*p)
757       break;
758     *p++ = 0;
759     while (*p && *p == ' ') p++;
760   }
761   ret = parse_injectargs(nargs, oss);
762   if (!nargs.empty()) {
763     *oss << " failed to parse arguments: ";
764     std::string prefix;
765     for (std::vector<const char*>::const_iterator i = nargs.begin();
766          i != nargs.end(); ++i) {
767       *oss << prefix << *i;
768       prefix = ",";
769     }
770     *oss << "\n";
771     ret = -EINVAL;
772   }
773   _apply_changes(oss);
774   return ret;
775 }
776
777 void md_config_t::set_val_or_die(const std::string &key,
778                                  const std::string &val,
779                                  bool meta)
780 {
781   std::stringstream err;
782   int ret = set_val(key, val, meta, &err);
783   if (ret != 0) {
784     std::cerr << "set_val_or_die(" << key << "): " << err.str();
785   }
786   assert(ret == 0);
787 }
788
789 int md_config_t::set_val(const std::string &key, const char *val,
790     bool meta, std::stringstream *err_ss)
791 {
792   Mutex::Locker l(lock);
793   if (key.empty()) {
794     if (err_ss) *err_ss << "No key specified";
795     return -EINVAL;
796   }
797   if (!val) {
798     return -EINVAL;
799   }
800
801   std::string v(val);
802   if (meta)
803     expand_meta(v, &std::cerr);
804
805   string k(ConfFile::normalize_key_name(key));
806
807   // subsystems?
808   if (strncmp(k.c_str(), "debug_", 6) == 0) {
809     for (size_t o = 0; o < subsys.get_num(); o++) {
810       std::string as_option = "debug_" + subsys.get_name(o);
811       if (k == as_option) {
812         int log, gather;
813         int r = sscanf(v.c_str(), "%d/%d", &log, &gather);
814         if (r >= 1) {
815           if (r < 2) {
816             gather = log;
817           }
818           subsys.set_log_level(o, log);
819           subsys.set_gather_level(o, gather);
820           if (err_ss) *err_ss << "Set " << k << " to " << log << "/" << gather;
821           return 0;
822         }
823         if (err_ss) {
824           *err_ss << "Invalid debug level, should be <int> or <int>/<int>";
825         }
826         return -EINVAL;
827       }
828     }   
829   }
830
831   const auto &opt_iter = schema.find(k);
832   if (opt_iter != schema.end()) {
833     const Option &opt = opt_iter->second;
834     if ((!opt.is_safe()) && internal_safe_to_start_threads) {
835       // If threads have been started and the option is not thread safe
836       if (observers.find(opt.name) == observers.end()) {
837         // And there is no observer to safely change it...
838         // You lose.
839         if (err_ss) *err_ss << "Configuration option '" << key << "' may "
840                     "not be modified at runtime";
841         return -ENOSYS;
842       }
843     }
844
845     std::string error_message;
846     int r = set_val_impl(v, opt, &error_message);
847     if (r == 0) {
848       if (err_ss) *err_ss << "Set " << opt.name << " to " << v;
849     } else {
850       if (err_ss) *err_ss << error_message;
851     }
852     return r;
853   }
854
855   if (err_ss) *err_ss << "Configuration option not found: '" << key << "'";
856   return -ENOENT;
857 }
858
859
860 int md_config_t::get_val(const std::string &key, char **buf, int len) const
861 {
862   Mutex::Locker l(lock);
863   return _get_val(key, buf,len);
864 }
865
866 Option::value_t md_config_t::get_val_generic(const std::string &key) const
867 {
868   Mutex::Locker l(lock);
869   return _get_val(key);
870 }
871
872 Option::value_t md_config_t::_get_val(const std::string &key) const
873 {
874   assert(lock.is_locked());
875
876   if (key.empty()) {
877     return Option::value_t(boost::blank());
878   }
879
880   // In key names, leading and trailing whitespace are not significant.
881   string k(ConfFile::normalize_key_name(key));
882
883   const auto &opt_iter = schema.find(k);
884   if (opt_iter != schema.end()) {
885     // Using .at() is safe because all keys in the schema always have
886     // entries in ::values
887     return values.at(k);
888   } else {
889     return Option::value_t(boost::blank());
890   }
891 }
892
893 int md_config_t::_get_val(const std::string &key, std::string *value) const {
894   assert(lock.is_locked());
895
896   std::string normalized_key(ConfFile::normalize_key_name(key));
897   Option::value_t config_value = _get_val(normalized_key.c_str());
898   if (!boost::get<boost::blank>(&config_value)) {
899     ostringstream oss;
900     if (bool *flag = boost::get<bool>(&config_value)) {
901       oss << (*flag ? "true" : "false");
902     } else if (double *dp = boost::get<double>(&config_value)) {
903       oss << std::fixed << *dp;
904     } else {
905       oss << config_value;
906     }
907     *value = oss.str();
908     return 0;
909   }
910   return -ENOENT;
911 }
912
913 int md_config_t::_get_val(const std::string &key, char **buf, int len) const
914 {
915   assert(lock.is_locked());
916
917   if (key.empty())
918     return -EINVAL;
919
920   string val ;
921   if (_get_val(key, &val) == 0) {
922     int l = val.length() + 1;
923     if (len == -1) {
924       *buf = (char*)malloc(l);
925       if (!*buf)
926         return -ENOMEM;
927       strncpy(*buf, val.c_str(), l);
928       return 0;
929     }
930     snprintf(*buf, len, "%s", val.c_str());
931     return (l > len) ? -ENAMETOOLONG : 0;
932   }
933
934   string k(ConfFile::normalize_key_name(key));
935   // subsys?
936   for (size_t o = 0; o < subsys.get_num(); o++) {
937     std::string as_option = "debug_" + subsys.get_name(o);
938     if (k == as_option) {
939       if (len == -1) {
940         *buf = (char*)malloc(20);
941         len = 20;
942       }
943       int l = snprintf(*buf, len, "%d/%d", subsys.get_log_level(o), subsys.get_gather_level(o));
944       return (l == len) ? -ENAMETOOLONG : 0;
945     }
946   }
947
948   // couldn't find a configuration option with key 'k'
949   return -ENOENT;
950 }
951
952 void md_config_t::get_all_keys(std::vector<std::string> *keys) const {
953   const std::string negative_flag_prefix("no_");
954
955   keys->clear();
956   keys->reserve(schema.size());
957   for (const auto &i: schema) {
958     const Option &opt = i.second;
959     keys->push_back(opt.name);
960     if (opt.type == Option::TYPE_BOOL) {
961       keys->push_back(negative_flag_prefix + opt.name);
962     }
963   }
964   for (size_t i = 0; i < subsys.get_num(); ++i) {
965     keys->push_back("debug_" + subsys.get_name(i));
966   }
967 }
968
969 /* The order of the sections here is important.  The first section in the
970  * vector is the "highest priority" section; if we find it there, we'll stop
971  * looking. The lowest priority section is the one we look in only if all
972  * others had nothing.  This should always be the global section.
973  */
974 void md_config_t::get_my_sections(std::vector <std::string> &sections) const
975 {
976   Mutex::Locker l(lock);
977   _get_my_sections(sections);
978 }
979
980 void md_config_t::_get_my_sections(std::vector <std::string> &sections) const
981 {
982   assert(lock.is_locked());
983   sections.push_back(name.to_str());
984
985   sections.push_back(name.get_type_name());
986
987   sections.push_back("global");
988 }
989
990 // Return a list of all sections
991 int md_config_t::get_all_sections(std::vector <std::string> &sections) const
992 {
993   Mutex::Locker l(lock);
994   for (ConfFile::const_section_iter_t s = cf.sections_begin();
995        s != cf.sections_end(); ++s) {
996     sections.push_back(s->first);
997   }
998   return 0;
999 }
1000
1001 int md_config_t::get_val_from_conf_file(const std::vector <std::string> &sections,
1002                     const std::string &key, std::string &out, bool emeta) const
1003 {
1004   Mutex::Locker l(lock);
1005   return _get_val_from_conf_file(sections, key, out, emeta);
1006 }
1007
1008 int md_config_t::_get_val_from_conf_file(const std::vector <std::string> &sections,
1009                                          const std::string &key, std::string &out, bool emeta) const
1010 {
1011   assert(lock.is_locked());
1012   std::vector <std::string>::const_iterator s = sections.begin();
1013   std::vector <std::string>::const_iterator s_end = sections.end();
1014   for (; s != s_end; ++s) {
1015     int ret = cf.read(s->c_str(), key, out);
1016     if (ret == 0) {
1017       if (emeta)
1018         expand_meta(out, &std::cerr);
1019       return 0;
1020     }
1021     else if (ret != -ENOENT)
1022       return ret;
1023   }
1024   return -ENOENT;
1025 }
1026
1027 int md_config_t::set_val_impl(const std::string &raw_val, const Option &opt,
1028                               std::string *error_message)
1029 {
1030   assert(lock.is_locked());
1031
1032   std::string val = raw_val;
1033
1034   int r = opt.pre_validate(&val, error_message);
1035   if (r != 0) {
1036     return r;
1037   }
1038
1039   Option::value_t new_value;
1040   if (opt.type == Option::TYPE_INT) {
1041     int64_t f = strict_si_cast<int64_t>(val.c_str(), error_message);
1042     if (!error_message->empty()) {
1043       return -EINVAL;
1044     }
1045     new_value = f;
1046   } else if (opt.type == Option::TYPE_UINT) {
1047     uint64_t f = strict_si_cast<uint64_t>(val.c_str(), error_message);
1048     if (!error_message->empty()) {
1049       return -EINVAL;
1050     }
1051     new_value = f;
1052   } else if (opt.type == Option::TYPE_STR) {
1053     new_value = val;
1054   } else if (opt.type == Option::TYPE_FLOAT) {
1055     double f = strict_strtod(val.c_str(), error_message);
1056     if (!error_message->empty()) {
1057       return -EINVAL;
1058     } else {
1059       new_value = f;
1060     }
1061   } else if (opt.type == Option::TYPE_BOOL) {
1062     if (strcasecmp(val.c_str(), "false") == 0) {
1063       new_value = false;
1064     } else if (strcasecmp(val.c_str(), "true") == 0) {
1065       new_value = true;
1066     } else {
1067       int b = strict_strtol(val.c_str(), 10, error_message);
1068       if (!error_message->empty()) {
1069         return -EINVAL;
1070       }
1071       new_value = !!b;
1072     }
1073   } else if (opt.type == Option::TYPE_ADDR) {
1074     entity_addr_t addr;
1075     if (!addr.parse(val.c_str())){
1076       return -EINVAL;
1077     }
1078     new_value = addr;
1079   } else if (opt.type == Option::TYPE_UUID) {
1080     uuid_d uuid;
1081     if (!uuid.parse(val.c_str())) {
1082       return -EINVAL;
1083     }
1084     new_value = uuid;
1085   } else {
1086     ceph_abort();
1087   }
1088
1089   r = opt.validate(new_value, error_message);
1090   if (r != 0) {
1091     return r;
1092   }
1093
1094
1095   // Apply the value to its entry in the `values` map
1096   values[opt.name] = new_value;
1097
1098   // Apply the value to its legacy field, if it has one
1099   auto legacy_ptr_iter = legacy_values.find(std::string(opt.name));
1100   if (legacy_ptr_iter != legacy_values.end()) {
1101     update_legacy_val(opt, legacy_ptr_iter->second);
1102   }
1103
1104   changed.insert(opt.name);
1105   return 0;
1106 }
1107
1108 /**
1109  * Handles assigning from a variant-of-types to a variant-of-pointers-to-types
1110  */
1111 class assign_visitor : public boost::static_visitor<>
1112 {
1113   md_config_t *conf;
1114   Option::value_t val;
1115   public:
1116
1117   assign_visitor(md_config_t *conf_, Option::value_t val_)
1118     : conf(conf_), val(val_)
1119   {}
1120
1121   template <typename T>
1122   void operator()( T md_config_t::* ptr) const
1123   {
1124     T *member = const_cast<T *>(&(conf->*(boost::get<const T md_config_t::*>(ptr))));
1125
1126     *member = boost::get<T>(val);
1127   }
1128 };
1129
1130 void md_config_t::update_legacy_val(const Option &opt,
1131                                     md_config_t::member_ptr_t member_ptr)
1132 {
1133   if (boost::get<boost::blank>(&values.at(opt.name))) {
1134     // This shouldn't happen, but if it does then just don't even
1135     // try to assign to the legacy field.
1136     return;
1137   }
1138
1139   boost::apply_visitor(assign_visitor(this, values.at(opt.name)), member_ptr);
1140 }
1141
1142
1143 static const char *CONF_METAVARIABLES[] = {
1144   "data_dir", // put this first: it may contain some of the others
1145   "cluster", "type", "name", "host", "num", "id", "pid", "cctid"
1146 };
1147 static const int NUM_CONF_METAVARIABLES =
1148       (sizeof(CONF_METAVARIABLES) / sizeof(CONF_METAVARIABLES[0]));
1149
1150 void md_config_t::expand_all_meta()
1151 {
1152   // Expand all metavariables
1153   ostringstream oss;
1154   for (const auto &i : schema) {
1155     const Option &opt = i.second;
1156
1157     if (opt.type == Option::TYPE_STR) {
1158       list<const Option*> stack;
1159       std::string *str = boost::get<std::string>(&(values.at(opt.name)));
1160       assert(str != nullptr);  // Non-string values should never get in
1161       expand_meta(*str, &opt, stack, &oss);
1162     }
1163   }
1164   cerr << oss.str();
1165 }
1166
1167 bool md_config_t::expand_meta(std::string &val,
1168                               std::ostream *oss) const
1169 {
1170   list<const Option*> stack;
1171   return expand_meta(val, NULL, stack, oss);
1172 }
1173
1174 bool md_config_t::expand_meta(std::string &origval,
1175                               const Option *opt,
1176                               std::list<const Option *> stack,
1177                               std::ostream *oss) const
1178 {
1179   assert(lock.is_locked());
1180
1181   // no $ means no variable expansion is necessary
1182   if (origval.find("$") == string::npos)
1183     return false;
1184
1185   // ignore an expansion loop and create a human readable
1186   // message about it
1187   if (opt) {
1188     for (const auto stack_ptr : stack) {
1189       if (opt->name == stack_ptr->name) {
1190         *oss << "variable expansion loop at "
1191              << opt->name << "=" << origval << std::endl;
1192         *oss << "expansion stack: " << std::endl;
1193         for (const auto j : stack) {
1194           std::string val;
1195           _get_val(j->name, &val);
1196           *oss << j->name << "=" << val << std::endl;
1197         }
1198         return false;
1199       }
1200     }
1201
1202     stack.push_front(opt);
1203   }
1204
1205   bool found_meta = false;
1206   string out;
1207   string val = origval;
1208   for (string::size_type s = 0; s < val.size(); ) {
1209     if (val[s] != '$') {
1210       out += val[s++];
1211       continue;
1212     }
1213
1214     // try to parse the variable name into var, either \$\{(.+)\} or
1215     // \$[a-z\_]+
1216     const char *valid_chars = "abcdefghijklmnopqrstuvwxyz_";
1217     string var;
1218     size_t endpos = 0;
1219     if (val[s+1] == '{') {
1220       // ...${foo_bar}...
1221       endpos = val.find_first_not_of(valid_chars, s+2);
1222       if (endpos != std::string::npos &&
1223           val[endpos] == '}') {
1224         var = val.substr(s+2, endpos-s-2);
1225         endpos++;
1226       }
1227     } else {
1228       // ...$foo...
1229       endpos = val.find_first_not_of(valid_chars, s+1);
1230       if (endpos != std::string::npos)
1231         var = val.substr(s+1, endpos-s-1);
1232       else
1233         var = val.substr(s+1);
1234     }
1235
1236     bool expanded = false;
1237     if (var.length()) {
1238       // special metavariable?
1239       for (int i = 0; i < NUM_CONF_METAVARIABLES; ++i) {
1240         if (var != CONF_METAVARIABLES[i])
1241           continue;
1242         //cout << "  meta match of " << var << " " << CONF_METAVARIABLES[i] << std::endl;
1243         if (var == "type")
1244           out += name.get_type_name();
1245         else if (var == "cluster")
1246           out += cluster;
1247         else if (var == "name")
1248           out += name.to_cstr();
1249         else if (var == "host")
1250         {
1251           if (host == "")
1252             out += ceph_get_short_hostname();
1253           else
1254             out += host;
1255         }
1256         else if (var == "num")
1257           out += name.get_id().c_str();
1258         else if (var == "id")
1259           out += name.get_id().c_str();
1260         else if (var == "pid")
1261           out += stringify(getpid());
1262         else if (var == "cctid")
1263           out += stringify((unsigned long long)this);
1264         else if (var == "data_dir") {
1265           if (data_dir_option.length()) {
1266             char *vv = NULL;
1267             _get_val(data_dir_option.c_str(), &vv, -1);
1268             string tmp = vv;
1269             free(vv);
1270             expand_meta(tmp, NULL, stack, oss);
1271             out += tmp;
1272           } else {
1273             // this isn't really right, but it'll result in a mangled
1274             // non-existent path that will fail any search list
1275             out += "$data_dir";
1276           }
1277         } else
1278           ceph_abort(); // unreachable
1279         expanded = true;
1280       }
1281
1282       if (!expanded) {
1283         // config option?
1284         const auto other_opt_iter = schema.find(var);
1285         if (other_opt_iter != schema.end()) {
1286           const Option &other_opt = other_opt_iter->second;
1287           if (other_opt.type == Option::TYPE_STR) {
1288             // The referenced option is a string, it may need substitution
1289             // before inserting.
1290             Option::value_t *other_val_ptr = const_cast<Option::value_t*>(&(values.at(other_opt.name)));
1291             std::string *other_opt_val = boost::get<std::string>(other_val_ptr);
1292             expand_meta(*other_opt_val, &other_opt, stack, oss);
1293             out += *other_opt_val;
1294           } else {
1295             // The referenced option is not a string: retrieve and insert
1296             // its stringized form.
1297             char *vv = NULL;
1298             _get_val(other_opt.name, &vv, -1);
1299             out += vv;
1300             free(vv);
1301           }
1302           expanded = true;
1303         }
1304       }
1305     }
1306
1307     if (expanded) {
1308       found_meta = true;
1309       s = endpos;
1310     } else {
1311       out += val[s++];
1312     }
1313   }
1314   // override the original value with the expanded value
1315   origval = out;
1316   return found_meta;
1317 }
1318
1319 void md_config_t::diff(
1320   const md_config_t *other,
1321   map<string, pair<string, string> > *diff,
1322   set<string> *unknown) 
1323 {
1324   diff_helper(other, diff, unknown);
1325 }
1326 void md_config_t::diff(
1327   const md_config_t *other,
1328   map<string, pair<string, string> > *diff,
1329   set<string> *unknown, const string& setting) 
1330 {
1331   diff_helper(other, diff, unknown, setting);
1332 }
1333
1334 void md_config_t::diff_helper(
1335     const md_config_t *other,
1336     map<string,pair<string,string> > *diff,
1337     set<string> *unknown, const string& setting)
1338 {
1339   Mutex::Locker l(lock);
1340
1341   char local_buf[4096];
1342   char other_buf[4096];
1343   for (const auto &i : schema) {
1344     const Option &opt = i.second;
1345     if (!setting.empty()) {
1346       if (setting != opt.name) {
1347         continue;
1348       }
1349     }
1350     memset(local_buf, 0, sizeof(local_buf));
1351     memset(other_buf, 0, sizeof(other_buf));
1352
1353     char *other_val = other_buf;
1354     int err = other->get_val(opt.name, &other_val, sizeof(other_buf));
1355     if (err < 0) {
1356       if (err == -ENOENT) {
1357         unknown->insert(opt.name);
1358       }
1359       continue;
1360     }
1361
1362     char *local_val = local_buf;
1363     err = _get_val(opt.name, &local_val, sizeof(local_buf));
1364     if (err != 0)
1365       continue;
1366
1367     if (strcmp(local_val, other_val))
1368       diff->insert(make_pair(opt.name, make_pair(local_val, other_val)));
1369     else if (!setting.empty()) {
1370         diff->insert(make_pair(opt.name, make_pair(local_val, other_val)));
1371         break;
1372     }
1373   }
1374 }
1375
1376 void md_config_t::complain_about_parse_errors(CephContext *cct)
1377 {
1378   ::complain_about_parse_errors(cct, &parse_errors);
1379 }
1380