*
*/
+#include "common/code_environment.h"
#include "common/config.h"
#include "common/DoutStreambuf.h"
#include "common/entity_name.h"
}
template <typename charT, typename traits>
-void DoutStreambuf<charT, traits>::read_global_config(const md_config_t *conf)
+const char** DoutStreambuf<charT, traits>::
+get_tracked_conf_keys() const
+{
+ static const char *KEYS[] =
+ { "log_file", "log_dir", "log_sym_dir",
+ "log_sym_history", "log_to_stderr",
+ "log_to_syslog", "log_per_instance", NULL };
+ return KEYS;
+}
+
+template <typename charT, typename traits>
+void DoutStreambuf<charT, traits>::
+handle_conf_change(const md_config_t *conf, const std::set <std::string> &changed)
{
DoutLocker _dout_locker;
type_name = conf->name.get_type_name();
}
if (conf->log_to_syslog) {
+ if ((changed.count("log_to_syslog") || changed.count("name")) &&
+ (g_code_env == CODE_ENVIRONMENT_DAEMON)) {
+ closelog();
+ openlog(g_conf.name.to_cstr(), LOG_ODELAY | LOG_PID, LOG_USER);
+ }
flags |= DOUTSB_FLAG_SYSLOG;
}
break;
default:
ostringstream oss;
- oss << "DoutStreambuf::read_global_config: can't understand "
+ oss << "DoutStreambuf::handle_conf_change: can't understand "
<< "conf->log_to_stderr = " << conf->log_to_stderr << "\n";
dout_emergency(oss.str());
break;
#ifndef CEPH_DOUT_STREAMBUF_H
#define CEPH_DOUT_STREAMBUF_H
+#include "common/config.h"
+
#include <iosfwd>
#include <string>
class md_config_t;
template <typename charT, typename traits = std::char_traits<charT> >
-class DoutStreambuf : public std::basic_streambuf<charT, traits>
+class DoutStreambuf : public std::basic_streambuf<charT, traits>,
+ public md_config_obs_t
{
public:
enum dout_streambuf_flags_t {
// for the error to happen.
void handle_stderr_closed();
- // Set the flags based on the global configuration
- void read_global_config(const md_config_t *conf);
+ virtual const char** get_tracked_conf_keys() const;
+
+ virtual void handle_conf_change(const md_config_t *conf,
+ const std::set <std::string> &changed);
// Set the priority of the messages being put into the stream
void set_prio(int prio);
return true;
}
-void parse_config_option_string(std::string& s)
-{
- char b[s.length()+1];
- strcpy(b, s.c_str());
- std::vector<const char*> nargs;
- char *p = b;
- while (*p) {
- nargs.push_back(p);
- while (*p && *p != ' ') p++;
- if (!*p)
- break;
- *p++ = 0;
- while (*p && *p == ' ') p++;
- }
- g_conf.parse_argv(nargs);
-}
-
// The defaults for CephInitParameters
CephInitParameters::CephInitParameters(uint32_t module_type, const char *conf_file_)
: conf_file(conf_file_)
int& argc, const char **&argv);
extern bool parse_ip_port_vec(const char *s, std::vector<entity_addr_t>& vec);
-extern void parse_config_option_string(std::string& s);
bool ceph_argparse_flag(std::vector<const char*> &args,
std::vector<const char*>::iterator &i, ...);
bool ceph_argparse_witharg(std::vector<const char*> &args,
#include <deque>
#include <syslog.h>
+#define _STR(x) #x
+#define STRINGIFY(x) _STR(x)
+
int keyring_init(md_config_t *conf)
{
if (!is_supported_auth(CEPH_AUTH_CEPHX))
// Create a configuration object
// TODO: de-globalize
md_config_t *conf = &g_conf; //new md_config_t();
+ // add config observers here
// Set up our entity name.
conf->name = iparams.name;
case CODE_ENVIRONMENT_DAEMON:
conf->daemonize = true;
if (!(flags & CINIT_FLAG_UNPRIVILEGED_DAEMON_DEFAULTS)) {
- conf->log_dir = "/var/log/ceph";
- conf->pid_file = "/var/run/ceph/$type.$id.pid";
+ conf->set_val_or_die("log_dir", "/var/log/ceph");
+ conf->set_val_or_die("pid_file", "/var/run/ceph/$type.$id.pid");
}
- conf->log_to_stderr = LOG_TO_STDERR_SOME;
+ conf->set_val_or_die("log_to_stderr", STRINGIFY(LOG_TO_STDERR_SOME));
break;
default:
- conf->daemonize = false;
+ conf->set_val_or_die("daemonize", "false");
break;
}
conf->log_per_instance = false;
}
- conf->expand_all_meta();
-
- if (conf->log_to_syslog || conf->clog_to_syslog) {
- closelog();
- openlog(g_conf.name.to_cstr(), LOG_ODELAY | LOG_PID, LOG_USER);
- }
-
- // Force a reopen of dout() with the configuration we have just read.
- _doss->read_global_config(&g_conf);
+ // Expand metavariables. Invoke configuration observers.
+ conf->apply_changes();
// Now we're ready to complain about config file parse errors
complain_about_parse_errors(&parse_errors);
#undef generic_dout
#undef dendl
+using std::map;
+using std::multimap;
+using std::pair;
+using std::set;
+using std::string;
+
const char *CEPH_CONF_FILE_DEFAULT = "/etc/ceph/ceph.conf, ~/.ceph/config, ceph.conf";
/* The Ceph configuration. */
config_option *opt = config_optionsp + i;
set_val_from_default(opt);
}
+
+ add_observer(_doss);
}
md_config_t::
{
}
+void md_config_t::
+add_observer(md_config_obs_t* observer_)
+{
+ const char **keys = observer_->get_tracked_conf_keys();
+ for (const char ** k = keys; *k; ++k) {
+ obs_map_t::value_type val(*k, observer_);
+ observers.insert(val);
+ }
+}
+
int md_config_t::
parse_config_files(const std::list<std::string> &conf_files,
std::deque<std::string> *parse_errors)
void md_config_t::
parse_argv(std::vector<const char*>& args)
{
+ // In this function, don't change any parts of g_conf directly.
+ // Instead, use set_val to set them. This will allow us to send the proper
+ // observer notifications later.
std::string val;
for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
if (ceph_argparse_flag(args, i, "--show_conf", (char*)NULL)) {
_exit(0);
}
else if (ceph_argparse_flag(args, i, "--foreground", "-f", (char*)NULL)) {
- daemonize = false;
- pid_file = "";
+ set_val_or_die("daemonize", "false");
+ set_val_or_die("pid_file", "");
}
else if (ceph_argparse_flag(args, i, "-d", (char*)NULL)) {
- daemonize = false;
- log_dir = "";
- pid_file = "";
- log_sym_dir = "";
- log_sym_history = 0;
- log_to_stderr = LOG_TO_STDERR_ALL;
- log_to_syslog = false;
- log_per_instance = false;
+ set_val_or_die("daemonize", "false");
+ set_val_or_die("log_dir", "");
+ set_val_or_die("pid_file", "");
+ set_val_or_die("log_sym_dir", "");
+ set_val_or_die("log_sym_history", "0");
+ set_val_or_die("log_to_stderr", STRINGIFY(LOG_TO_STDERR_ALL));
+ set_val_or_die("log_to_syslog", "false");
+ set_val_or_die("log_per_instance", "false");
}
// Some stuff that we wanted to give universal single-character options for
// Careful: you can burn through the alphabet pretty quickly by adding
// to this list.
else if (ceph_argparse_witharg(args, i, &val, "--monmap", "-M", (char*)NULL)) {
- monmap = val;
+ set_val("monmap", val.c_str());
}
else if (ceph_argparse_witharg(args, i, &val, "--mon_host", "-m", (char*)NULL)) {
- mon_host = val;
+ set_val("mon_host", val.c_str());
}
else if (ceph_argparse_witharg(args, i, &val, "--bind", (char*)NULL)) {
- public_addr.parse(val.c_str());
+ set_val("public_addr", val.c_str());
}
else if (ceph_argparse_witharg(args, i, &val, "--keyfile", "-K", (char*)NULL)) {
- keyfile = val;
+ set_val("keyfile", val.c_str());
}
else if (ceph_argparse_witharg(args, i, &val, "--keyring", "-k", (char*)NULL)) {
- keyring = val;
+ set_val("keyring", val.c_str());
}
else if (ceph_argparse_witharg(args, i, &val, "--client_mountpoint", "-r", (char*)NULL)) {
- client_mountpoint = val;
+ set_val("client_mountpoint", val.c_str());
}
else {
int o;
}
}
}
+
+}
+
+void md_config_t::
+apply_changes()
+{
+ /* Maps observers to the configuration options that they care about which
+ * have changed. */
+ typedef std::map < md_config_obs_t*, std::set <std::string> > rev_obs_map_t;
+
+ // Expand all metavariables
+ for (int i = 0; i < NUM_CONFIG_OPTIONS; i++) {
+ config_option *opt = config_optionsp + i;
+ if (opt->type == OPT_STR) {
+ std::string *str = (std::string *)opt->conf_ptr(this);
+ expand_meta(*str);
+ }
+ }
+
+ // create the reverse observer mapping, mapping observers to the set of
+ // changed keys that they'll get.
+ rev_obs_map_t robs;
+ std::set <std::string> empty_set;
+ for (changed_set_t::const_iterator c = changed.begin();
+ c != changed.end(); ++c) {
+ const std::string &key(*c);
+ pair < obs_map_t::iterator, obs_map_t::iterator >
+ range(observers.equal_range(key));
+ for (obs_map_t::iterator r = range.first; r != range.second; ++r) {
+ rev_obs_map_t::value_type robs_val(r->second, empty_set);
+ pair < rev_obs_map_t::iterator, bool > robs_ret(robs.insert(robs_val));
+ std::set <std::string> &keys(robs_ret.first->second);
+ keys.insert(key);
+ }
+ }
+
+ // Make any pending observer callbacks
+ for (rev_obs_map_t::const_iterator r = robs.begin(); r != robs.end(); ++r) {
+ md_config_obs_t *obs = r->first;
+ obs->handle_conf_change(this, r->second);
+ }
+
+ changed.clear();
+}
+
+void md_config_t::
+injectargs(const std::string& s)
+{
+ char b[s.length()+1];
+ strcpy(b, s.c_str());
+ std::vector<const char*> nargs;
+ char *p = b;
+ while (*p) {
+ nargs.push_back(p);
+ while (*p && *p != ' ') p++;
+ if (!*p)
+ break;
+ *p++ = 0;
+ while (*p && *p == ' ') p++;
+ }
+ parse_argv(nargs);
+ apply_changes();
+}
+
+void md_config_t::
+set_val_or_die(const char *key, const char *val)
+{
+ int ret = set_val(key, val);
+ assert(ret == 0);
}
int md_config_t::
return -ENOENT;
}
+
int md_config_t::
get_val(const char *key, char **buf, int len) const
{
void md_config_t::
set_val_from_default(const config_option *opt)
{
+ // set_val_from_default can't fail! Unless the programmer screwed up, and
+ // in that case we'll abort.
+ // Anyway, we know that this function changed something.
+ changed.insert(opt->name);
+
switch (opt->type) {
case OPT_INT:
*(int*)opt->conf_ptr(this) = opt->def_longlong;
int md_config_t::
set_val_impl(const char *val, const config_option *opt)
+{
+ int ret = set_val_raw(val, opt);
+ if (ret)
+ return ret;
+ changed.insert(opt->name);
+ return 0;
+}
+
+int md_config_t::
+set_val_raw(const char *val, const config_option *opt)
{
switch (opt->type) {
case OPT_INT: {
return -ENOSYS;
}
-void md_config_t::
-expand_all_meta()
-{
- for (int i = 0; i < NUM_CONFIG_OPTIONS; i++) {
- config_option *opt = config_optionsp + i;
- if (opt->type == OPT_STR) {
- std::string *str = (std::string *)opt->conf_ptr(this);
- expand_meta(*str);
- }
- }
-}
-
static const char *CONF_METAVARIABLES[] =
{ "type", "name", "host", "num", "id" };
static const int NUM_CONF_METAVARIABLES =
val = out;
return found_meta;
}
+
+md_config_obs_t::
+~md_config_obs_t()
+{
+}
#include <vector>
#include <map>
+#include <set>
#include "common/ConfUtils.h"
#include "common/entity_name.h"
#define OSD_REP_CHAIN 2
class config_option;
+class md_config_obs_t;
extern const char *CEPH_CONF_FILE_DEFAULT;
-enum log_to_stderr_t {
- LOG_TO_STDERR_NONE = 0,
- LOG_TO_STDERR_SOME = 1,
- LOG_TO_STDERR_ALL = 2,
-};
+#define LOG_TO_STDERR_NONE 0
+#define LOG_TO_STDERR_SOME 1
+#define LOG_TO_STDERR_ALL 2
template <typename T, typename U>
class DoutStreambuf;
-struct md_config_t
-{
+struct md_config_t {
public:
+ /* Maps configuration options to the observer listening for them. */
+ typedef std::multimap <std::string, md_config_obs_t*> obs_map_t;
+
+ /* Set of configuration options that have changed since the last
+ * apply_changes */
+ typedef std::set < std::string > changed_set_t;
+
+ // Create a new md_config_t structure.
md_config_t();
~md_config_t();
+ // Adds a new observer to this configuration. You can do this at any time,
+ // but it will only receive notifications for the changes that happen after
+ // you attach it, obviously.
+ //
+ // Most developers will probably attach their observers after common_init,
+ // but before anyone can call injectargs.
+ //
+ // The caller is responsible for allocating observers.
+ void add_observer(md_config_obs_t* observer_);
+
// Parse a config file
int parse_config_files(const std::list<std::string> &conf_files,
std::deque<std::string> *parse_errors);
// Absorb config settings from argv
void parse_argv(std::vector<const char*>& args);
+ // Expand all metavariables. Make any pending observer callbacks.
+ void apply_changes();
+
+ // Called by the Ceph daemons to make configuration changes at runtime
+ void injectargs(const std::string &s);
+
+ // Set a configuration value, or crash
+ // Metavariables will be expanded.
+ void set_val_or_die(const char *key, const char *val);
+
// Set a configuration value.
// Metavariables will be expanded.
int set_val(const char *key, const char *val);
int get_val_from_conf_file(const std::vector <std::string> §ions,
const char *key, std::string &out, bool emeta) const;
- // Perform metavariable expansion on all the data members of md_config_t.
- void expand_all_meta();
-
// Expand metavariables in the provided string.
// Returns true if any metavariables were found and expanded.
bool expand_meta(std::string &val) const;
void set_val_from_default(const config_option *opt);
int set_val_impl(const char *val, const config_option *opt);
+ int set_val_raw(const char *val, const config_option *opt);
// The configuration file we read, or NULL if we haven't read one.
ConfFile cf;
+ obs_map_t observers;
+ changed_set_t changed;
+
public:
std::string host;
const void *conf_ptr(const md_config_t *conf) const;
};
+class md_config_obs_t {
+public:
+ virtual ~md_config_obs_t();
+ virtual const char** get_tracked_conf_keys() const = 0;
+ virtual void handle_conf_change(const md_config_t *conf,
+ const std::set <std::string> &changed) = 0;
+};
+
#include "common/debug.h"
#endif
void sighup_handler(int signum)
{
- // do nothing
- // logger_reopen_all
+ /* In the past, users had to send a SIGHUP to the process after making a
+ * change to certain parts of the logging configuration. Now, this is no
+ * longer necessary. Now we want to ignore SIGHUP signals.
+ */
}
static void reraise_fatal(int signum)
* Returns 0 on success, error code otherwise. */
int rados_conf_set(rados_t cluster, const char *option, const char *value);
-/* Reopens the log file.
- * You must do this after changing the logging configuration.
- * It is also good practice to call this from your SIGHUP signal handler, so that users can send you
- * a SIGHUP to reopen the log.
- */
-void rados_reopen_log(rados_t cluster);
-
/* Returns a configuration value as a string.
* If len is positive, that is the maximum number of bytes we'll write into the
* buffer. If len == -1, we'll call malloc() and set *buf.
void shutdown();
int conf_read_file(const char * const path) const;
int conf_set(const char *option, const char *value);
- void reopen_log();
int conf_get(const char *option, std::string &val);
int pool_create(const char *name);
return rados_conf_set((rados_t)client, option, value);
}
-void librados::Rados::
-reopen_log()
-{
- rados_reopen_log((rados_t)client);
-}
-
int librados::Rados::
conf_get(const char *option, std::string &val)
{
// configuration
md_config_t *conf = common_preinit(iparams, CODE_ENVIRONMENT_LIBRARY, 0);
conf->parse_env(); // environment variables override
- conf->expand_all_meta(); // future proofing
+ conf->apply_changes();
++rados_initialized;
}
if (ret)
return ret;
g_conf.parse_env(); // environment variables override
- g_conf.expand_all_meta(); // handle metavariables in the config
+ g_conf.apply_changes();
complain_about_parse_errors(&parse_errors);
-
return 0;
}
extern "C" int rados_conf_set(rados_t cluster, const char *option, const char *value)
{
- return g_conf.set_val(option, value);
-}
-
-extern "C" void rados_reopen_log(rados_t cluster)
-{
- sighup_handler(SIGHUP);
+ int ret = g_conf.set_val(option, value);
+ if (ret)
+ return ret;
+ g_conf.apply_changes();
+ return 0;
}
/* cluster info */
{
dout(10) << "handle_command args: " << m->cmd << dendl;
if (m->cmd[0] == "injectargs")
- parse_config_option_string(m->cmd[1]);
+ g_conf.injectargs(m->cmd[1]);
else if (m->cmd[0] == "dumpcache") {
if (m->cmd.size() > 1)
mdcache->dump_cache(m->cmd[1].c_str());
if (m->cmd[0] == "_injectargs") {
dout(0) << "parsing injected options '" << m->cmd[1] << "'" << dendl;
- parse_config_option_string(m->cmd[1]);
+ g_conf.injectargs(m->cmd[1]);
return;
}
if (m->cmd[0] == "class") {
dout(20) << "handle_command args: " << m->cmd << dendl;
if (m->cmd[0] == "injectargs")
- parse_config_option_string(m->cmd[1]);
+ g_conf.injectargs(m->cmd[1]);
else if (m->cmd[0] == "stop") {
dout(0) << "got shutdown" << dendl;
shutdown();
if (ret != 0):
raise make_ex(ret, "error calling conf_set")
- def reopen_log(self):
- self.require_state("configuring", "connected")
- self.librados.rados_reopen_log(self.cluster);
-
def connect(self):
self.require_state("configuring")
ret = self.librados.rados_connect(self.cluster)
#include "common/config.h"
#include <iostream>
+#include <set>
#include <sstream>
#include <string>
#include <syslog.h>
DoutStreambuf<char> *dos = new DoutStreambuf<char>();
{
+ std::set <std::string> changed;
+ for (const char** t = dos->get_tracked_conf_keys(); *t; ++t) {
+ changed.insert(*t);
+ }
DoutLocker _dout_locker;
- dos->read_global_config(&g_conf);
+ dos->handle_conf_change(&g_conf, changed);
}
derr << "using configuration: " << dos->config_to_str() << dendl;
assert(rados_create(&cluster, NULL) == 0);
assert(rados_conf_read_file(cluster, NULL) == 0);
- rados_reopen_log(cluster);
assert(rados_connect(cluster) == 0);
if (rados_pool_lookup(cluster, TEST_POOL) != -ENOENT) {
rbd = new librbd::RBD();
assert(rados.init(NULL) == 0);
assert(rados.conf_read_file(NULL) == 0);
- rados.reopen_log();
assert(rados.connect() == 0);
if (rados.pool_lookup(TEST_POOL) != -ENOENT) {
int r = rados.pool_delete(TEST_POOL);
printf("error: error setting log_to_stderr\n");
exit(1);
}
- rados_reopen_log(cl);
if (rados_conf_get(cl, "log to stderr", tmp, sizeof(tmp))) {
printf("error: failed to read log_to_stderr from config\n");
exit(1);
printf("error: error setting log_to_stderr\n");
exit(1);
}
- rados.reopen_log();
std::string tmp;
if (rados.conf_get("log to stderr", tmp)) {
printf("error: failed to read log_to_stderr from config\n");