1    	// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2    	
3    	#pragma once
4    	
5    	#include <type_traits>
6    	#include "common/config.h"
7    	#include "common/config_obs.h"
8    	#include "common/config_obs_mgr.h"
9    	#include "common/ceph_mutex.h"
10   	
11   	// @c ConfigProxy is a facade of multiple config related classes. it exposes
12   	// the legacy settings with arrow operator, and the new-style config with its
13   	// member methods.
14   	class ConfigProxy {
15   	  static ConfigValues get_config_values(const ConfigProxy &config_proxy) {
16   	    std::lock_guard locker(config_proxy.lock);
17   	    return config_proxy.values;
18   	  }
19   	
20   	  /**
21   	   * The current values of all settings described by the schema
22   	   */
23   	  ConfigValues values;
24   	  using md_config_obs_t = ceph::md_config_obs_impl<ConfigProxy>;
25   	  ObserverMgr<md_config_obs_t> obs_mgr;
26   	  md_config_t config;
27   	  /** A lock that protects the md_config_t internals. It is
28   	   * recursive, for simplicity.
29   	   * It is best if this lock comes first in the lock hierarchy. We will
30   	   * hold this lock when calling configuration observers.  */
31   	  mutable ceph::recursive_mutex lock =
32   	    ceph::make_recursive_mutex("ConfigProxy::lock");
33   	
34   	  class CallGate {
35   	  private:
36   	    uint32_t call_count = 0;
37   	    ceph::mutex lock;
38   	    ceph::condition_variable cond;
39   	  public:
40   	    CallGate()
41   	      : lock(ceph::make_mutex("call::gate::lock")) {
42   	    }
43   	
44   	    void enter() {
45   	      std::lock_guard<ceph::mutex> locker(lock);
46   	      ++call_count;
47   	    }
48   	    void leave() {
49   	      std::lock_guard<ceph::mutex> locker(lock);
50   	      ceph_assert(call_count > 0);
51   	      if (--call_count == 0) {
52   	        cond.notify_all();
53   	      }
54   	    }
55   	    void close() {
56   	      std::unique_lock<ceph::mutex> locker(lock);
57   	      while (call_count != 0) {
58   	        cond.wait(locker);
59   	      }
60   	    }
61   	  };
62   	
63   	  void call_gate_enter(md_config_obs_t *obs) {
64   	    auto p = obs_call_gate.find(obs);
65   	    ceph_assert(p != obs_call_gate.end());
66   	    p->second->enter();
67   	  }
68   	  void call_gate_leave(md_config_obs_t *obs) {
69   	    auto p = obs_call_gate.find(obs);
70   	    ceph_assert(p != obs_call_gate.end());
71   	    p->second->leave();
72   	  }
73   	  void call_gate_close(md_config_obs_t *obs) {
74   	    auto p = obs_call_gate.find(obs);
75   	    ceph_assert(p != obs_call_gate.end());
76   	    p->second->close();
77   	  }
78   	
79   	  using rev_obs_map_t = ObserverMgr<md_config_obs_t>::rev_obs_map;
80   	  typedef std::unique_ptr<CallGate> CallGateRef;
81   	
82   	  std::map<md_config_obs_t*, CallGateRef> obs_call_gate;
83   	
84   	  void call_observers(std::unique_lock<ceph::recursive_mutex>& locker,
85   	                      rev_obs_map_t& rev_obs) {
86   	    // observers are notified outside of lock
87   	    locker.unlock();
88   	    for (auto& [obs, keys] : rev_obs) {
89   	      obs->handle_conf_change(*this, keys);
90   	    }
91   	    locker.lock();
92   	
93   	    for (auto& rev_ob : rev_obs) {
94   	      call_gate_leave(rev_ob.first);
95   	    }
96   	  }
97   	
98   	  void map_observer_changes(md_config_obs_t *obs, const std::string &key,
99   	                            rev_obs_map_t *rev_obs) {
100  	    ceph_assert(ceph_mutex_is_locked(lock));
101  	
102  	    auto [it, new_entry] = rev_obs->emplace(obs, std::set<std::string>{});
103  	    it->second.emplace(key);
104  	    if (new_entry) {
105  	      // this needs to be done under lock as once this lock is
106  	      // dropped (before calling observers) a remove_observer()
107  	      // can sneak in and cause havoc.
108  	      call_gate_enter(obs);
109  	    }
110  	  }
111  	
112  	public:
113  	  explicit ConfigProxy(bool is_daemon)
114  	    : config{values, obs_mgr, is_daemon}
115  	  {}
116  	  explicit ConfigProxy(const ConfigProxy &config_proxy)
117  	    : values(get_config_values(config_proxy)),
118  	      config{values, obs_mgr, config_proxy.config.is_daemon}
119  	  {}
120  	  const ConfigValues* operator->() const noexcept {
121  	    return &values;
122  	  }
123  	  ConfigValues* operator->() noexcept {
124  	    return &values;
125  	  }
126  	  int get_val(const std::string_view key, char** buf, int len) const {
127  	    std::lock_guard l{lock};
128  	    return config.get_val(values, key, buf, len);
129  	  }
130  	  int get_val(const std::string_view key, std::string *val) const {
131  	    std::lock_guard l{lock};
132  	    return config.get_val(values, key, val);
133  	  }
134  	  template<typename T>
135  	  const T get_val(const std::string_view key) const {
136  	    std::lock_guard l{lock};
137  	    return config.template get_val<T>(values, key);
138  	  }
139  	  template<typename T, typename Callback, typename...Args>
140  	  auto with_val(const std::string_view key, Callback&& cb, Args&&... args) const {
141  	    std::lock_guard l{lock};
142  	    return config.template with_val<T>(values, key,
143  					       std::forward<Callback>(cb),
144  					       std::forward<Args>(args)...);
145  	  }
146  	  void config_options(ceph::Formatter *f) const {
147  	    config.config_options(f);
148  	  }
149  	  const decltype(md_config_t::schema)& get_schema() const {
150  	    return config.schema;
151  	  }
152  	  const Option* get_schema(const std::string_view key) const {
153  	    auto found = config.schema.find(key);
154  	    if (found == config.schema.end()) {
155  	      return nullptr;
156  	    } else {
157  	      return &found->second;
158  	    }
159  	  }
160  	  const Option *find_option(const std::string& name) const {
161  	    return config.find_option(name);
162  	  }
163  	  void diff(ceph::Formatter *f, const std::string& name = {}) const {
164  	    std::lock_guard l{lock};
165  	    return config.diff(values, f, name);
166  	  }
167  	  void get_my_sections(std::vector <std::string> &sections) const {
168  	    std::lock_guard l{lock};
169  	    config.get_my_sections(values, sections);
170  	  }
171  	  int get_all_sections(std::vector<std::string>& sections) const {
172  	    std::lock_guard l{lock};
173  	    return config.get_all_sections(sections);
174  	  }
175  	  int get_val_from_conf_file(const std::vector<std::string>& sections,
176  				     const std::string_view key, std::string& out,
177  				     bool emeta) const {
178  	    std::lock_guard l{lock};
179  	    return config.get_val_from_conf_file(values,
180  						 sections, key, out, emeta);
181  	  }
182  	  unsigned get_osd_pool_default_min_size(uint8_t size) const {
183  	    return config.get_osd_pool_default_min_size(values, size);
184  	  }
185  	  void early_expand_meta(std::string &val,
186  				 std::ostream *oss) const {
187  	    std::lock_guard l{lock};
188  	    return config.early_expand_meta(values, val, oss);
189  	  }
190  	  // for those want to reexpand special meta, e.g, $pid
191  	  void finalize_reexpand_meta() {
192  	    std::unique_lock locker(lock);
193  	    rev_obs_map_t rev_obs;
194  	    if (config.finalize_reexpand_meta(values, obs_mgr)) {
195  	      _gather_changes(values.changed, &rev_obs, nullptr);
196  	      values.changed.clear();
197  	    }
198  	
199  	    call_observers(locker, rev_obs);
200  	  }
201  	  void add_observer(md_config_obs_t* obs) {
202  	    std::lock_guard l(lock);
203  	    obs_mgr.add_observer(obs);
204  	    obs_call_gate.emplace(obs, std::make_unique<CallGate>());
205  	  }
206  	  void remove_observer(md_config_obs_t* obs) {
(1) Event fun_call_w_exception: Called function throws an exception of type "_ZN5boost16exception_detail10clone_implINS0_19error_info_injectorINSt8ios_base7failureB5cxx11EEEEE". [details]
207  	    std::lock_guard l(lock);
208  	    call_gate_close(obs);
209  	    obs_call_gate.erase(obs);
210  	    obs_mgr.remove_observer(obs);
211  	  }
212  	  void call_all_observers() {
213  	    std::unique_lock locker(lock);
214  	    rev_obs_map_t rev_obs;
215  	    obs_mgr.for_each_observer(
216  	      [this, &rev_obs](md_config_obs_t *obs, const std::string &key) {
217  	        map_observer_changes(obs, key, &rev_obs);
218  	      });
219  	
220  	    call_observers(locker, rev_obs);
221  	  }
222  	  void set_safe_to_start_threads() {
223  	    config.set_safe_to_start_threads();
224  	  }
225  	  void _clear_safe_to_start_threads() {
226  	    config._clear_safe_to_start_threads();
227  	  }
228  	  void show_config(std::ostream& out) {
229  	    std::lock_guard l{lock};
230  	    config.show_config(values, out);
231  	  }
232  	  void show_config(ceph::Formatter *f) {
233  	    std::lock_guard l{lock};
234  	    config.show_config(values, f);
235  	  }
236  	  void config_options(ceph::Formatter *f) {
237  	    std::lock_guard l{lock};
238  	    config.config_options(f);
239  	  }
240  	  int rm_val(const std::string_view key) {
241  	    std::lock_guard l{lock};
242  	    return config.rm_val(values, key);
243  	  }
244  	  // Expand all metavariables. Make any pending observer callbacks.
245  	  void apply_changes(std::ostream* oss) {
246  	    std::unique_lock locker(lock);
247  	    rev_obs_map_t rev_obs;
248  	
249  	    // apply changes until the cluster name is assigned
250  	    if (!values.cluster.empty()) {
251  	      // meta expands could have modified anything.  Copy it all out again.
252  	      _gather_changes(values.changed, &rev_obs, oss);
253  	      values.changed.clear();
254  	    }
255  	
256  	    call_observers(locker, rev_obs);
257  	  }
258  	  void _gather_changes(std::set<std::string> &changes,
259  	                       rev_obs_map_t *rev_obs, std::ostream* oss) {
260  	    obs_mgr.for_each_change(
261  	      changes, *this,
262  	      [this, rev_obs](md_config_obs_t *obs, const std::string &key) {
263  	        map_observer_changes(obs, key, rev_obs);
264  	      }, oss);
265  	  }
266  	  int set_val(const std::string_view key, const std::string& s,
267  	              std::stringstream* err_ss=nullptr) {
268  	    std::lock_guard l{lock};
269  	    return config.set_val(values, obs_mgr, key, s, err_ss);
270  	  }
271  	  void set_val_default(const std::string_view key, const std::string& val) {
272  	    std::lock_guard l{lock};
273  	    config.set_val_default(values, obs_mgr, key, val);
274  	  }
275  	  void set_val_or_die(const std::string_view key, const std::string& val) {
276  	    std::lock_guard l{lock};
277  	    config.set_val_or_die(values, obs_mgr, key, val);
278  	  }
279  	  int set_mon_vals(CephContext *cct,
280  			   const std::map<std::string,std::string,std::less<>>& kv,
281  			   md_config_t::config_callback config_cb) {
282  	    std::unique_lock locker(lock);
283  	    int ret = config.set_mon_vals(cct, values, obs_mgr, kv, config_cb);
284  	
285  	    rev_obs_map_t rev_obs;
286  	    _gather_changes(values.changed, &rev_obs, nullptr);
287  	    values.changed.clear();
288  	
289  	    call_observers(locker, rev_obs);
290  	    return ret;
291  	  }
292  	  int injectargs(const std::string &s, std::ostream *oss) {
293  	    std::unique_lock locker(lock);
294  	    int ret = config.injectargs(values, obs_mgr, s, oss);
295  	
296  	    rev_obs_map_t rev_obs;
297  	    _gather_changes(values.changed, &rev_obs, oss);
298  	    values.changed.clear();
299  	
300  	    call_observers(locker, rev_obs);
301  	    return ret;
302  	  }
303  	  void parse_env(unsigned entity_type,
304  			 const char *env_var = "CEPH_ARGS") {
305  	    std::lock_guard l{lock};
306  	    config.parse_env(entity_type, values, obs_mgr, env_var);
307  	  }
308  	  int parse_argv(std::vector<const char*>& args, int level=CONF_CMDLINE) {
309  	    std::lock_guard l{lock};
310  	    return config.parse_argv(values, obs_mgr, args, level);
311  	  }
312  	  int parse_config_files(const char *conf_files,
313  				 std::ostream *warnings, int flags) {
314  	    std::lock_guard l{lock};
315  	    return config.parse_config_files(values, obs_mgr,
316  					     conf_files, warnings, flags);
317  	  }
318  	  bool has_parse_error() const {
319  	    return !config.parse_error.empty();
320  	  }
321  	  void complain_about_parse_error(CephContext *cct) {
322  	    return config.complain_about_parse_error(cct);
323  	  }
324  	  void do_argv_commands() const {
325  	    std::lock_guard l{lock};
326  	    config.do_argv_commands(values);
327  	  }
328  	  void get_config_bl(uint64_t have_version,
329  			     ceph::buffer::list *bl,
330  			     uint64_t *got_version) {
331  	    std::lock_guard l{lock};
332  	    config.get_config_bl(values, have_version, bl, got_version);
333  	  }
334  	  void get_defaults_bl(ceph::buffer::list *bl) {
335  	    std::lock_guard l{lock};
336  	    config.get_defaults_bl(values, bl);
337  	  }
338  	};
339