summaryrefslogtreecommitdiff
path: root/server/monitor
diff options
context:
space:
mode:
authorStephen Gallagher <sgallagh@redhat.com>2009-04-11 10:20:48 -0400
committerSimo Sorce <ssorce@redhat.com>2009-04-13 09:06:49 -0400
commit4626af1ad1141291f226382f3569e4dd0486cd08 (patch)
treef35309c400cd028482750e0469db218e6eb02d49 /server/monitor
parentc1d6bffe9ba81c265042859dddf3b39be87c161b (diff)
downloadsssd-4626af1ad1141291f226382f3569e4dd0486cd08.tar.gz
sssd-4626af1ad1141291f226382f3569e4dd0486cd08.tar.bz2
sssd-4626af1ad1141291f226382f3569e4dd0486cd08.zip
Allow configuration of the SSSD through /etc/sssd/sssd.conf
The SSSD now links with the ini_config and collection libraries in the common directory. The monitor will track changes to the /etc/sssd/sssd.conf file using inotify on platforms that support it, or polled every 5 seconds on platforms that do not. At startup or modification of the conf file, the monitor will purge the existing confdb and reread it completely from the conf file, to ensure that there are no lingering entries. It does this in a transaction, so there should be no race condition with the client services. A new option has been added to the startup options for the SSSD. It is now possible to specify an alternate config file with the -c <file> at the command line.
Diffstat (limited to 'server/monitor')
-rw-r--r--server/monitor/monitor.c353
-rw-r--r--server/monitor/monitor.h7
2 files changed, 347 insertions, 13 deletions
diff --git a/server/monitor/monitor.c b/server/monitor/monitor.c
index 65664ca6..16811224 100644
--- a/server/monitor/monitor.c
+++ b/server/monitor/monitor.c
@@ -24,6 +24,14 @@
#include <sys/wait.h>
#include <sys/time.h>
#include <time.h>
+#include "config.h"
+#ifdef HAVE_SYS_INOTIFY_H
+#include <sys/inotify.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
#include "util/util.h"
#include "popt.h"
#include "tevent.h"
@@ -79,6 +87,17 @@ struct mt_ctx {
int service_id_timeout;
};
+struct config_file_ctx {
+ TALLOC_CTX *parent_ctx;
+ struct confdb_ctx *cdb;
+ struct tevent_context *ev;
+ int fd;
+ char *filename;
+ time_t modified;
+ confdb_reconf_fn reconf_fn;
+ void *reconf_pvt;
+};
+
static int start_service(struct mt_svc *mt_svc);
static int dbus_service_init(struct sbus_conn_ctx *conn_ctx, void *data);
@@ -97,8 +116,11 @@ static int get_service_config(struct mt_ctx *ctx, const char *name,
struct mt_svc **svc_cfg);
static int get_provider_config(struct mt_ctx *ctx, const char *name,
struct mt_svc **svc_cfg);
-static int add_new_service (struct mt_ctx *ctx, const char *name);
-static int add_new_provider (struct mt_ctx *ctx, const char *name);
+static int add_new_service(struct mt_ctx *ctx, const char *name);
+static int add_new_provider(struct mt_ctx *ctx, const char *name);
+
+static int monitor_signal_reconf(struct confdb_ctx *cdb, void *pvt);
+static int update_monitor_config(struct mt_ctx *ctx);
/* dbus_get_monitor_version
* Return the monitor version over D-BUS */
@@ -474,6 +496,13 @@ done:
dbus_message_unref(reply);
}
+static int monitor_signal_reconf(struct confdb_ctx *cdb, void *pvt)
+{
+ struct mt_ctx *ctx = talloc_get_type(pvt, struct mt_ctx);
+ DEBUG(1, ("Configuration has changed. Reloading.\n"));
+ return update_monitor_config(ctx);
+}
+
static int service_signal_reload(struct mt_svc *svc)
{
DBusMessage *msg;
@@ -513,6 +542,11 @@ static int service_signal_reload(struct mt_svc *svc)
int get_monitor_config(struct mt_ctx *ctx)
{
int ret;
+ size_t svc_count = 0;
+ char *svcs;
+ char *cur, *p, *t;
+ char **svc_list = NULL;
+ char **tmp_list = NULL;
ret = confdb_get_int(ctx->cdb, ctx,
MONITOR_CONF_ENTRY, "sbusTimeout",
@@ -521,22 +555,61 @@ int get_monitor_config(struct mt_ctx *ctx)
return ret;
}
- ret = confdb_get_param(ctx->cdb, ctx,
- "config/services", "activeServices",
- &ctx->services);
+ ret = confdb_get_string(ctx->cdb, ctx,
+ "config/services", "activeServices",
+ NULL, &svcs);
- if (ctx->services[0] == NULL) {
+ if (ret != EOK || svcs == NULL) {
DEBUG(0, ("No services configured!\n"));
return EINVAL;
}
+ cur = p = talloc_strdup(svcs, svcs);
+ while (p && *p) {
+ for (cur = p; (*cur == ' ' || *cur == '\t'); cur++) /* trim */ ;
+ if (!*cur) break;
+
+ p = strchr(cur, ',');
+ if (p) {
+ /* terminate element */
+ *p = '\0';
+ /* trim spaces */
+ for (t = p-1; (*t == ' ' || *t == '\t'); t--) *t = '\0';
+ p++;
+ }
+
+ svc_count++;
+ tmp_list = talloc_realloc(svcs, svc_list, char *, svc_count);
+ if (!tmp_list) {
+ ret = ENOMEM;
+ goto done;
+ }
+ svc_list = tmp_list;
+ svc_list[svc_count-1] = talloc_strdup(svc_list, cur);
+ }
+
+ svc_count++;
+ tmp_list = talloc_realloc(svcs, svc_list, char *, svc_count);
+ if (!tmp_list) {
+ ret = ENOMEM;
+ goto done;
+ }
+ svc_list = tmp_list;
+ svc_list[svc_count-1] = NULL;
+
+ ctx->services = talloc_steal(ctx, svc_list);
+
ret = confdb_get_domains(ctx->cdb, ctx, &ctx->domains);
if (ret != EOK) {
DEBUG(2, ("No domains configured. LOCAL should always exist!\n"));
return ret;
}
- return EOK;
+ ret = EOK;
+
+done:
+ talloc_free(svcs);
+ return ret;
}
static int get_service_config(struct mt_ctx *ctx, const char *name,
@@ -604,7 +677,7 @@ static int get_service_config(struct mt_ctx *ctx, const char *name,
return EOK;
}
-static int add_new_service (struct mt_ctx *ctx, const char *name)
+static int add_new_service(struct mt_ctx *ctx, const char *name)
{
int ret;
struct mt_svc *svc;
@@ -701,7 +774,7 @@ static int get_provider_config(struct mt_ctx *ctx, const char *name,
return EOK;
}
-static int add_new_provider (struct mt_ctx *ctx, const char *name)
+static int add_new_provider(struct mt_ctx *ctx, const char *name)
{
int ret;
struct mt_svc *svc;
@@ -958,9 +1031,233 @@ static void monitor_hup(struct tevent_context *ev,
update_monitor_config(ctx);
}
+#ifdef HAVE_SYS_INOTIFY_H
+static void config_file_changed(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags, void *data)
+{
+ int ret;
+ TALLOC_CTX *tmp_ctx;
+ struct inotify_event *in_event;
+ char *buf;
+ char *name;
+ ssize_t len, total_len;
+ ssize_t event_size = sizeof(struct inotify_event);
+ struct config_file_ctx *file_ctx =
+ talloc_get_type(data, struct config_file_ctx);
+
+ DEBUG(1, ("Config file changed\n"));
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return;
+
+ buf = talloc_size(tmp_ctx, event_size);
+ if (!buf) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ total_len = 0;
+ while (total_len < event_size) {
+ len = read(file_ctx->fd, &buf+total_len, event_size-total_len);
+ if (len == -1 && errno != EINTR) {
+ DEBUG(0, ("Critical error reading inotify file descriptor.\n"));
+ talloc_free(tmp_ctx);
+ return;
+ }
+ total_len += len;
+ }
+
+ in_event = (struct inotify_event *)&buf;
+
+ if (in_event->len > 0) {
+ /* Read in the name, even though we don't use it,
+ * so that read ptr is in the right place
+ */
+ name = talloc_size(tmp_ctx, len);
+ total_len = 0;
+ while (total_len < in_event->len) {
+ len = read(file_ctx->fd, &name, in_event->len);
+ if (len == -1 && errno != EINTR) {
+ DEBUG(0, ("Critical error reading inotify file descriptor.\n"));
+ talloc_free(tmp_ctx);
+ return;
+ }
+ total_len += len;
+ }
+ }
+
+ /* Parse the configuration file */
+ ret = confdb_init_db(file_ctx->filename, file_ctx->cdb);
+ if (ret != EOK) {
+ DEBUG(0, ("Could not reload configuration!"));
+ kill(getpid(), SIGTERM);
+ }
+
+ /* Tell the monitor to signal the children */
+ file_ctx->reconf_fn(file_ctx->cdb, file_ctx->reconf_pvt);
+
+ talloc_free(tmp_ctx);
+}
+#endif
+
+static void poll_config_file(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t, void *ptr)
+{
+ int ret, err;
+ struct stat file_stat;
+ struct timeval tv;
+ struct tevent_timer *timer;
+ struct config_file_ctx *file_ctx =
+ talloc_get_type(ptr,struct config_file_ctx);
+
+ ret = stat(file_ctx->filename, &file_stat);
+ if (ret < 0) {
+ err = errno;
+ DEBUG(0, ("Could not stat file [%s]. Error [%d:%s]\n",
+ file_ctx->filename, err, strerror(err)));
+ /* TODO: If the config file is missing, should we shut down? */
+ return;
+ }
+
+ if (file_stat.st_mtime != file_ctx->modified) {
+ /* Parse the configuration file and signal the children */
+ /* Note: this will fire if the modification time changes into the past
+ * as well as the future.
+ */
+ DEBUG(1, ("Config file changed\n"));
+ file_ctx->modified = file_stat.st_mtime;
+
+ ret = confdb_init_db(file_ctx->filename, file_ctx->cdb);
+ if (ret != EOK) {
+ DEBUG(0, ("Could not reload configuration!"));
+ kill(getpid(), SIGTERM);
+ }
+
+ /* Tell the monitor to signal the children */
+ file_ctx->reconf_fn(file_ctx->cdb, file_ctx->reconf_pvt);
+ }
+
+ gettimeofday(&tv, NULL);
+ tv.tv_sec += CONFIG_FILE_POLL_INTERVAL;
+ tv.tv_usec = 0;
+ timer = tevent_add_timer(ev, file_ctx->parent_ctx, tv,
+ poll_config_file, file_ctx);
+ if (!timer) {
+ DEBUG(0, ("Error: Config file no longer monitored for changes!"));
+ }
+}
+
+static int try_inotify(struct config_file_ctx *file_ctx)
+{
+#ifdef HAVE_SYS_INOTIFY_H
+ int fd, wd, err, fd_args, ret;
+ struct tevent_fd *tfd;
+
+ /* Set up inotify to monitor the config file for changes */
+ fd = inotify_init();
+ if (fd < 0) {
+ err = errno;
+ DEBUG(0, ("Could not initialize inotify, error [%d:%s]\n",
+ err, strerror(err)));
+ return err;
+ }
+
+ fd_args = fcntl(fd, F_GETFL, NULL);
+ if (fd_args < 0) {
+ /* Could not set nonblocking */
+ close(fd);
+ return EINVAL;
+ }
+
+ fd_args |= O_NONBLOCK;
+ ret = fcntl(fd, F_SETFL, fd_args);
+ if (ret < 0) {
+ /* Could not set nonblocking */
+ close(fd);
+ return EINVAL;
+ }
+
+ wd = inotify_add_watch(fd, file_ctx->filename, IN_MODIFY);
+ if (wd < 0) {
+ err = errno;
+ DEBUG(0, ("Could not add inotify watch for file [%s]. Error [%d:%s]\n",
+ file_ctx->filename, err, strerror(err)));
+ close(fd);
+ return err;
+ }
+
+ /* Add the inotify file descriptor to the TEvent context */
+ file_ctx->fd = fd;
+
+ tfd = tevent_add_fd(file_ctx->ev, file_ctx, fd, TEVENT_FD_READ,
+ config_file_changed, file_ctx);
+ if (!tfd) {
+ inotify_rm_watch(fd, wd);
+ close(fd);
+ return EIO;
+ }
+ return EOK;
+#else
+ return EINVAL;
+#endif
+}
+
+static int monitor_config_file(TALLOC_CTX *mem_ctx,
+ struct confdb_ctx *cdb,
+ struct tevent_context *ev,
+ const char *file,
+ confdb_reconf_fn fn,
+ void *reconf_pvt)
+{
+ int ret, err;
+ struct timeval tv;
+
+ struct stat file_stat;
+ struct config_file_ctx *file_ctx;
+
+ struct tevent_timer *timer;
+
+ ret = stat(file, &file_stat);
+ if (ret < 0) {
+ err = errno;
+ DEBUG(0, ("Could not stat file [%s]. Error [%d:%s]\n",
+ file, err, strerror(err)));
+ return err;
+ }
+
+ file_ctx = talloc_zero(mem_ctx, struct config_file_ctx);
+ if (!file_ctx) return ENOMEM;
+
+ file_ctx->parent_ctx = mem_ctx;
+ file_ctx->cdb = cdb;
+ file_ctx->filename = talloc_strdup(file_ctx, file);
+ file_ctx->modified = file_stat.st_mtime;
+ file_ctx->reconf_fn = fn;
+ file_ctx->reconf_pvt = reconf_pvt;
+ file_ctx->ev = ev;
+
+ ret = try_inotify(file_ctx);
+ if (ret != EOK) {
+ /* Could not monitor file with inotify, fall back to polling */
+ gettimeofday(&tv, NULL);
+ tv.tv_sec += CONFIG_FILE_POLL_INTERVAL;
+ tv.tv_usec = 0;
+ timer = tevent_add_timer(ev, mem_ctx, tv, poll_config_file, file_ctx);
+ if (!timer) {
+ talloc_free(file_ctx);
+ return EIO;
+ }
+ }
+
+ return EOK;
+}
+
int monitor_process_init(TALLOC_CTX *mem_ctx,
struct tevent_context *event_ctx,
- struct confdb_ctx *cdb)
+ struct confdb_ctx *cdb,
+ const char *config_file)
{
struct mt_ctx *ctx;
struct sysdb_ctx *sysdb;
@@ -976,10 +1273,37 @@ int monitor_process_init(TALLOC_CTX *mem_ctx,
ctx->ev = event_ctx;
ctx->cdb = cdb;
+ /* Initialize the CDB from the configuration file */
+ ret = confdb_test(ctx->cdb);
+ if (ret == ENOENT) {
+ /* First-time setup
+ * Load special entries
+ */
+ ret = confdb_create_base(cdb);
+ if (ret != EOK) {
+ talloc_free(ctx);
+ return ret;
+ }
+ }
+
+ ret = confdb_init_db(config_file, cdb);
+ if (ret != EOK) {
+ talloc_free(cdb);
+ DEBUG(0, ("ConfDB initialization has failed [%s]\n",
+ strerror(ret)));
+ talloc_free(ctx);
+ return ret;
+ }
+
+ /* Read in the monitor's configuration */
ret = get_monitor_config(ctx);
if (ret != EOK)
return ret;
+ /* Watch for changes to the confdb config file */
+ ret = monitor_config_file(ctx, cdb, event_ctx, config_file, monitor_signal_reconf, ctx);
+ if (ret != EOK) return ret;
+
/* Avoid a startup race condition between InfoPipe
* and NSS. If the sysdb doesn't exist yet, both
* will try to create it at the same time. So
@@ -1165,6 +1489,8 @@ static void identity_check(DBusPendingCall *pending, void *data)
goto done;
}
+ DEBUG(4,("Received ID reply: (%s,%d)\n", svc_name, svc_ver));
+
/* search this service in the list */
svc = fake_svc->mt_ctx->svc_list;
while (svc) {
@@ -1546,6 +1872,7 @@ int main(int argc, const char *argv[])
poptContext pc;
int opt_daemon = 0;
int opt_interactive = 0;
+ char *opt_config_file = NULL;
int flags = 0;
struct main_context *main_ctx;
int ret;
@@ -1557,6 +1884,8 @@ int main(int argc, const char *argv[])
"Become a daemon (default)", NULL }, \
{"interactive", 'i', POPT_ARG_NONE, &opt_interactive, 0, \
"Run interactive (not a daemon)", NULL}, \
+ {"config", 'c', POPT_ARG_STRING, &opt_config_file, 0, \
+ "Specify a non-default config file", NULL}, \
{ NULL }
};
@@ -1581,6 +1910,7 @@ int main(int argc, const char *argv[])
if (opt_daemon) flags |= FLAGS_DAEMON;
if (opt_interactive) flags |= FLAGS_INTERACTIVE;
+ if (!opt_config_file) opt_config_file = CONFDB_DEFAULT_CONFIG_FILE;
/* we want a pid file check */
flags |= FLAGS_PID_FILE;
@@ -1591,7 +1921,8 @@ int main(int argc, const char *argv[])
ret = monitor_process_init(main_ctx,
main_ctx->event_ctx,
- main_ctx->confdb_ctx);
+ main_ctx->confdb_ctx,
+ opt_config_file);
if (ret != EOK) return 3;
/* loop on main */
diff --git a/server/monitor/monitor.h b/server/monitor/monitor.h
index 77b6e781..2a5218b1 100644
--- a/server/monitor/monitor.h
+++ b/server/monitor/monitor.h
@@ -1,4 +1,4 @@
-/*
+/*
SSSD
Service monitor
@@ -22,8 +22,11 @@
#ifndef _MONITOR_H_
#define _MONITOR_H_
+#define CONFIG_FILE_POLL_INTERVAL 5 /* seconds */
+
int monitor_process_init(TALLOC_CTX *mem_ctx,
struct tevent_context *event_ctx,
- struct confdb_ctx *cdb);
+ struct confdb_ctx *cdb,
+ const char *config_file);
#endif /* _MONITOR_H */