From d9f203e045c63c853ae60b47fb8013e92600c9f9 Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Mon, 19 Jan 2009 13:33:28 -0500 Subject: Refactoring the monitor code and SBUS utility functions. --- server/configure.ac | 2 +- server/monitor.c | 1005 ----------------------------------- server/monitor.h | 29 - server/monitor/monitor.c | 1005 +++++++++++++++++++++++++++++++++++ server/monitor/monitor.h | 29 + server/monitor/monitor_interfaces.h | 42 ++ server/monitor/monitor_sbus.c | 97 ++++ server/monitor/monitor_sbus.h | 30 ++ server/nss/nsssrv.c | 33 +- server/nss/nsssrv_dp.c | 23 +- server/providers/data_provider.c | 33 +- server/providers/data_provider.h | 4 +- server/providers/data_provider_be.c | 70 ++- server/providers/dp_interfaces.h | 32 ++ server/providers/dp_sbus.c | 98 ++++ server/providers/dp_sbus.h | 30 ++ server/sbus/sbus_client.c | 79 +++ server/sbus/sbus_client.h | 42 ++ server/sbus_interfaces.h | 49 -- server/server.mk | 7 +- server/util/service_helpers.c | 91 ---- server/util/service_helpers.h | 39 -- 22 files changed, 1621 insertions(+), 1248 deletions(-) delete mode 100644 server/monitor.c delete mode 100644 server/monitor.h create mode 100644 server/monitor/monitor.c create mode 100644 server/monitor/monitor.h create mode 100644 server/monitor/monitor_interfaces.h create mode 100644 server/monitor/monitor_sbus.c create mode 100644 server/monitor/monitor_sbus.h create mode 100644 server/providers/dp_interfaces.h create mode 100644 server/providers/dp_sbus.c create mode 100644 server/providers/dp_sbus.h create mode 100644 server/sbus/sbus_client.c create mode 100644 server/sbus/sbus_client.h delete mode 100644 server/sbus_interfaces.h delete mode 100644 server/util/service_helpers.c delete mode 100644 server/util/service_helpers.h (limited to 'server') diff --git a/server/configure.ac b/server/configure.ac index 0d02fae8..ddf9d47b 100644 --- a/server/configure.ac +++ b/server/configure.ac @@ -12,7 +12,7 @@ AC_DEFUN([SMB_LIBRARY_ENABLE], [echo -n ""]) AC_DEFUN([SMB_EXT_LIB], [echo -n ""]) AC_DEFUN([SMB_ENABLE], [echo -n ""]) AC_INIT(ldb, 0.9.2) -AC_CONFIG_SRCDIR([monitor.c]) +AC_CONFIG_SRCDIR([autogen.sh]) AC_LIBREPLACE_ALL_CHECKS diff --git a/server/monitor.c b/server/monitor.c deleted file mode 100644 index 36904791..00000000 --- a/server/monitor.c +++ /dev/null @@ -1,1005 +0,0 @@ -/* - SSSD - - Service monitor - - Copyright (C) Simo Sorce 2008 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include "popt.h" -#include "tevent.h" -#include "util/util.h" -#include "confdb/confdb.h" -#include "monitor.h" -#include "dbus/dbus.h" -#include "sbus/sssd_dbus.h" -#include "sbus_interfaces.h" - -/* ping time cannot be less then once every few seconds or the - * monitor will get crazy hammering children with messages */ -#define MONITOR_DEF_PING_TIME 10 - -struct mt_conn { - struct sbus_conn_ctx *conn_ctx; - struct mt_svc *svc_ptr; -}; - -struct mt_svc { - struct mt_svc *prev; - struct mt_svc *next; - - struct mt_conn *mt_conn; - struct mt_ctx *mt_ctx; - - char *command; - char *name; - pid_t pid; - - int ping_time; - - int restarts; - time_t last_restart; - time_t last_pong; -}; - -struct mt_ctx { - struct event_context *ev; - struct confdb_ctx *cdb; - char **services; - struct mt_svc *svc_list; - struct sbus_srv_ctx *sbus_srv; - - int service_id_timeout; -}; - -static int start_service(struct mt_svc *mt_svc); - -static int dbus_service_init(struct sbus_conn_ctx *conn_ctx, void *data); -static void identity_check(DBusPendingCall *pending, void *data); - -static int service_send_ping(struct mt_svc *svc); -static void ping_check(DBusPendingCall *pending, void *data); - -static int service_check_alive(struct mt_svc *svc); - -static void set_tasks_checker(struct mt_svc *srv); - -/* dbus_get_monitor_version - * Return the monitor version over D-BUS */ -static int dbus_get_monitor_version(DBusMessage *message, - void *data, - DBusMessage **r) -{ - const char *version = MONITOR_VERSION; - DBusMessage *reply; - dbus_bool_t ret; - - reply = dbus_message_new_method_return(message); - ret = dbus_message_append_args(reply, DBUS_TYPE_STRING, - &version, DBUS_TYPE_INVALID); - - if (!ret) { - return EIO; - } - - *r = reply; - return EOK; -} - -struct sbus_method monitor_methods[] = { - { MONITOR_METHOD_VERSION, dbus_get_monitor_version}, - {NULL, NULL} -}; - -/* monitor_dbus_init - * Set up the monitor service as a D-BUS Server */ -static int monitor_dbus_init(struct mt_ctx *ctx) -{ - struct sbus_method_ctx *sd_ctx; - struct sbus_srv_ctx *sbus_srv; - char *sbus_address; - char *default_monitor_address; - int ret; - - default_monitor_address = talloc_asprintf(ctx, "unix:path=%s/%s", - PIPE_PATH, SSSD_SERVICE_PIPE); - if (!default_monitor_address) { - return ENOMEM; - } - - ret = confdb_get_string(ctx->cdb, ctx, - "config/services/monitor", "sbusAddress", - default_monitor_address, &sbus_address); - if (ret != EOK) { - talloc_free(default_monitor_address); - return ret; - } - talloc_free(default_monitor_address); - - sd_ctx = talloc_zero(ctx, struct sbus_method_ctx); - if (!sd_ctx) { - talloc_free(sbus_address); - return ENOMEM; - } - - /* Set up globally-available D-BUS methods */ - sd_ctx->interface = talloc_strdup(sd_ctx, MONITOR_DBUS_INTERFACE); - if (!sd_ctx->interface) { - talloc_free(sbus_address); - talloc_free(sd_ctx); - return ENOMEM; - } - sd_ctx->path = talloc_strdup(sd_ctx, MONITOR_DBUS_PATH); - if (!sd_ctx->path) { - talloc_free(sbus_address); - talloc_free(sd_ctx); - return ENOMEM; - } - sd_ctx->methods = monitor_methods; - sd_ctx->message_handler = sbus_message_handler; - - ret = sbus_new_server(ctx, ctx->ev, sd_ctx, &sbus_srv, sbus_address, dbus_service_init, ctx); - ctx->sbus_srv = sbus_srv; - - return ret; -} - -static void tasks_check_handler(struct event_context *ev, - struct timed_event *te, - struct timeval t, void *ptr) -{ - struct mt_svc *svc = talloc_get_type(ptr, struct mt_svc); - time_t now = time(NULL); - bool process_alive = true; - int ret; - - ret = service_check_alive(svc); - switch (ret) { - case EOK: - /* all fine */ - break; - - case ECHILD: - DEBUG(1,("Process (%s) is stopped!\n", svc->name)); - process_alive = false; - break; - - default: - /* TODO: should we tear down it ? */ - DEBUG(1,("Checking for service %s(%d) failed!!\n", - svc->name, svc->pid)); - break; - } - - if (process_alive) { - ret = service_send_ping(svc); - switch (ret) { - case EOK: - /* all fine */ - break; - - case ENXIO: - DEBUG(1,("Child (%s) not responding! (yet)\n", svc->name)); - break; - - default: - /* TODO: should we tear it down ? */ - DEBUG(1,("Sending a message to service (%s) failed!!\n", svc->name)); - break; - } - - if (svc->last_pong != 0) { - if ((now - svc->last_pong) > 30) { /* TODO: get val from config */ - /* too long since we last heard of this process */ - ret = kill(svc->pid, SIGUSR1); - if (ret != EOK) { - DEBUG(0,("Sending signal to child (%s:%d) failed! " - "Ignore and pretend child is dead.\n", - svc->name, svc->pid)); - } - process_alive = false; - } - } - - } - - if (!process_alive) { - DLIST_REMOVE(svc->mt_ctx->svc_list, svc); - if (svc->last_restart != 0) { - if ((now - svc->last_restart) > 30) { /* TODO: get val from config */ - /* it was long ago reset restart threshold */ - svc->restarts = 0; - } - } - - /* restart the process */ - if (svc->restarts > 3) { /* TODO: get val from config */ - DEBUG(0, ("Process [%s], definitely stopped!\n", svc->name)); - talloc_free(svc); - return; - } - - ret = start_service(svc); - if (ret != EOK) { - DEBUG(0,("Failed to restart service '%s'\n", svc->name)); - talloc_free(svc); - return; - } - - svc->restarts++; - svc->last_restart = now; - return; - } - - /* all fine, set up the task checker again */ - set_tasks_checker(svc); -} - -static void set_tasks_checker(struct mt_svc *svc) -{ - struct timed_event *te = NULL; - struct timeval tv; - - gettimeofday(&tv, NULL); - tv.tv_sec += svc->ping_time; - tv.tv_usec = 0; - te = event_add_timed(svc->mt_ctx->ev, svc, tv, tasks_check_handler, svc); - if (te == NULL) { - DEBUG(0, ("failed to add event, monitor offline for [%s]!\n", - svc->name)); - /* FIXME: shutdown ? */ - } -} - -int get_monitor_config(struct mt_ctx *ctx) -{ - int ret; - - ret = confdb_get_int(ctx->cdb, ctx, - "config/services/monitor", "sbusTimeout", - -1, &ctx->service_id_timeout); - if (ret != EOK) { - return ret; - } - - ret = confdb_get_param(ctx->cdb, ctx, - "config/services", "activeServices", - &ctx->services); - - if (ctx->services[0] == NULL) { - DEBUG(0, ("No services configured!\n")); - return EINVAL; - } - - return EOK; -} - -int monitor_process_init(TALLOC_CTX *mem_ctx, - struct event_context *event_ctx, - struct confdb_ctx *cdb) -{ - struct mt_ctx *ctx; - struct mt_svc *svc; - char **doms; - char *path; - int ret, i; - - ctx = talloc_zero(mem_ctx, struct mt_ctx); - if (!ctx) { - DEBUG(0, ("fatal error initializing monitor!\n")); - return ENOMEM; - } - ctx->ev = event_ctx; - ctx->cdb = cdb; - - ret = get_monitor_config(ctx); - if (ret != EOK) - return ret; - - /* Initialize D-BUS Server - * The monitor will act as a D-BUS server for all - * SSSD processes */ - ret = monitor_dbus_init(ctx); - if (ret != EOK) { - return ret; - } - - /* start all services */ - for (i = 0; ctx->services[i]; i++) { - - svc = talloc_zero(ctx, struct mt_svc); - if (!svc) { - talloc_free(ctx); - return ENOMEM; - } - svc->name = ctx->services[i]; - svc->mt_ctx = ctx; - - path = talloc_asprintf(svc, "config/services/%s", svc->name); - if (!path) { - talloc_free(ctx); - return ENOMEM; - } - - ret = confdb_get_string(cdb, svc, path, "command", NULL, &svc->command); - if (ret != EOK) { - DEBUG(0,("Failed to start service '%s'\n", svc->name)); - talloc_free(svc); - continue; - } - - ret = confdb_get_int(cdb, svc, path, "timeout", - MONITOR_DEF_PING_TIME, &svc->ping_time); - if (ret != EOK) { - DEBUG(0,("Failed to start service '%s'\n", svc->name)); - talloc_free(svc); - continue; - } - - talloc_free(path); - - /* Add this service to the queue to be started once the monitor - * enters its mainloop. - */ - ret = start_service(svc); - if (ret != EOK) { - DEBUG(0,("Failed to start service '%s'\n", svc->name)); - talloc_free(svc); - continue; - } - } - - /* now start the data providers */ - ret = confdb_get_domains(cdb, ctx, &doms); - if (ret != EOK) { - DEBUG(2, ("No domains configured. LOCAL should always exist!\n")); - return ret; - } - - for (i = 0; doms[i]; i++) { - svc = talloc_zero(ctx, struct mt_svc); - if (!svc) { - talloc_free(ctx); - return ENOMEM; - } - svc->name = talloc_asprintf(svc, "%%BE_%s", doms[i]); - svc->mt_ctx = ctx; - - path = talloc_asprintf(svc, "config/domains/%s", doms[i]); - if (!path) { - talloc_free(ctx); - return ENOMEM; - } - ret = confdb_get_string(cdb, svc, path, - "command", NULL, &svc->command); - if (ret != EOK) { - DEBUG(0, ("Failed to find provider [%s] configuration\n", doms[i])); - talloc_free(svc); - continue; - } - - ret = confdb_get_int(cdb, svc, path, "timeout", - MONITOR_DEF_PING_TIME, &svc->ping_time); - if (ret != EOK) { - DEBUG(0,("Failed to start service '%s'\n", svc->name)); - talloc_free(svc); - continue; - } - - talloc_free(path); - - /* if no command is present do not run the domain */ - if (svc->command == NULL) { - /* the LOCAL domain does not need a backend at the moment */ - if (strcasecmp(doms[i], "LOCAL") != 0) { - DEBUG(0, ("Missing command to run provider\n")); - } - talloc_free(svc); - continue; - } - - ret = start_service(svc); - if (ret != EOK) { - DEBUG(0,("Failed to start provider for '%s'\n", doms[i])); - talloc_free(svc); - continue; - } - - DLIST_ADD(ctx->svc_list, svc); - - set_tasks_checker(svc); - } - - return EOK; -} - -static int mt_conn_destructor(void *ptr) -{ - struct mt_conn *mt_conn; - struct mt_svc *svc; - - mt_conn = talloc_get_type(ptr, struct mt_conn); - svc = mt_conn->svc_ptr; - - /* now clear up so that the rest of the code will know there - * is no connection attached to the service anymore */ - svc->mt_conn = NULL; - - return 0; -} - -/* - * dbus_service_init - * This function should initiate a query to the newly connected - * service to discover the service's identity (invoke the getIdentity - * method on the new client). The reply callback for this request - * should set the connection destructor appropriately. - */ -static int dbus_service_init(struct sbus_conn_ctx *conn_ctx, void *data) -{ - struct mt_ctx *ctx; - struct mt_svc *svc; - struct mt_conn *mt_conn; - DBusMessage *msg; - DBusPendingCall *pending_reply; - DBusConnection *conn; - DBusError dbus_error; - dbus_bool_t dbret; - - DEBUG(3, ("Initializing D-BUS Service\n")); - - ctx = talloc_get_type(data, struct mt_ctx); - conn = sbus_get_connection(conn_ctx); - dbus_error_init(&dbus_error); - - /* hang off this memory to the connection so that when the connection - * is freed we can call a destructor to clear up the structure and - * have a way to know we need to restart the service */ - mt_conn = talloc(conn_ctx, struct mt_conn); - if (!mt_conn) { - DEBUG(0,("Out of memory?!\n")); - talloc_free(conn_ctx); - return ENOMEM; - } - mt_conn->conn_ctx = conn_ctx; - - /* at this stage we still do not know what service is this - * we will know only after we get its identity, so we make - * up a temporary fake service and complete the operation - * when we receive the reply */ - svc = talloc_zero(mt_conn, struct mt_svc); - if (!svc) { - talloc_free(conn_ctx); - return ENOMEM; - } - svc->mt_ctx = ctx; - svc->mt_conn = mt_conn; - - mt_conn->svc_ptr = svc; - talloc_set_destructor((TALLOC_CTX *)mt_conn, mt_conn_destructor); - - /* - * Set up identity request - * This should be a well-known path and method - * for all services - */ - msg = dbus_message_new_method_call(NULL, - SERVICE_PATH, - SERVICE_INTERFACE, - SERVICE_METHOD_IDENTITY); - if (msg == NULL) { - DEBUG(0,("Out of memory?!\n")); - talloc_free(conn_ctx); - return ENOMEM; - } - dbret = dbus_connection_send_with_reply(conn, msg, &pending_reply, - ctx->service_id_timeout); - if (!dbret) { - /* - * Critical Failure - * We can't communicate on this connection - * We'll drop it using the default destructor. - */ - DEBUG(0, ("D-BUS send failed.\n")); - dbus_message_unref(msg); - talloc_free(conn_ctx); - return EIO; - } - - /* Set up the reply handler */ - dbus_pending_call_set_notify(pending_reply, identity_check, svc, NULL); - dbus_message_unref(msg); - - return EOK; -} - -static void identity_check(DBusPendingCall *pending, void *data) -{ - struct mt_svc *fake_svc; - struct mt_svc *svc; - struct sbus_conn_ctx *conn_ctx; - DBusMessage *reply; - DBusError dbus_error; - dbus_uint16_t svc_ver; - char *svc_name; - dbus_bool_t ret; - int type; - - fake_svc = talloc_get_type(data, struct mt_svc); - conn_ctx = fake_svc->mt_conn->conn_ctx; - dbus_error_init(&dbus_error); - - reply = dbus_pending_call_steal_reply(pending); - if (!reply) { - /* reply should never be null. This function shouldn't be called - * until reply is valid or timeout has occurred. If reply is NULL - * here, something is seriously wrong and we should bail out. - */ - DEBUG(0, ("Serious error. A reply callback was called but no reply was received and no timeout occurred\n")); - - /* Destroy this connection */ - sbus_disconnect(conn_ctx); - goto done; - } - - type = dbus_message_get_type(reply); - switch (type) { - case DBUS_MESSAGE_TYPE_METHOD_RETURN: - ret = dbus_message_get_args(reply, &dbus_error, - DBUS_TYPE_STRING, &svc_name, - DBUS_TYPE_UINT16, &svc_ver, - DBUS_TYPE_INVALID); - if (!ret) { - DEBUG(1,("Failed, to parse message, killing connection\n")); - sbus_disconnect(conn_ctx); - goto done; - } - - /* search this service in the list */ - svc = fake_svc->mt_ctx->svc_list; - while (svc) { - ret = strcasecmp(svc->name, svc_name); - if (ret == 0) { - break; - } - svc = svc->next; - } - if (!svc) { - DEBUG(0,("Unable to find peer in list of services, killing connection!\n")); - sbus_disconnect(conn_ctx); - goto done; - } - - /* transfer all from the fake service and get rid of it */ - fake_svc->mt_conn->svc_ptr = svc; - svc->mt_conn = fake_svc->mt_conn; - talloc_free(fake_svc); - - /* Set up the destructor for this service */ - break; - - case DBUS_MESSAGE_TYPE_ERROR: - DEBUG(0,("getIdentity returned an error [%s], closing connection.\n", - dbus_message_get_error_name(reply))); - /* Falling through to default intentionally*/ - default: - /* - * Timeout or other error occurred or something - * unexpected happened. - * It doesn't matter which, because either way we - * know that this connection isn't trustworthy. - * We'll destroy it now. - */ - sbus_disconnect(conn_ctx); - return; - } - -done: - dbus_pending_call_unref(pending); - dbus_message_unref(reply); -} - -/* service_send_ping - * this function send a dbus ping to a service. - * It returns EOK if all is fine or ENXIO if the connection is - * not available (either not yet set up or teared down). - * Returns e generic error in other cases. - */ -static int service_send_ping(struct mt_svc *svc) -{ - DBusMessage *msg; - DBusPendingCall *pending_reply; - DBusConnection *conn; - DBusError dbus_error; - dbus_bool_t dbret; - - if (!svc->mt_conn) { - return ENXIO; - } - - DEBUG(4,("Pinging %s\n", svc->name)); - - conn = sbus_get_connection(svc->mt_conn->conn_ctx); - dbus_error_init(&dbus_error); - - /* - * Set up identity request - * This should be a well-known path and method - * for all services - */ - msg = dbus_message_new_method_call(NULL, - SERVICE_PATH, - SERVICE_INTERFACE, - SERVICE_METHOD_PING); - if (!msg) { - DEBUG(0,("Out of memory?!\n")); - talloc_free(svc->mt_conn->conn_ctx); - return ENOMEM; - } - - dbret = dbus_connection_send_with_reply(conn, msg, &pending_reply, - svc->mt_ctx->service_id_timeout); - if (!dbret) { - /* - * Critical Failure - * We can't communicate on this connection - * We'll drop it using the default destructor. - */ - DEBUG(0, ("D-BUS send failed.\n")); - talloc_free(svc->mt_conn->conn_ctx); - return EIO; - } - - /* Set up the reply handler */ - dbus_pending_call_set_notify(pending_reply, ping_check, svc, NULL); - dbus_message_unref(msg); - - return EOK; -} - -static void ping_check(DBusPendingCall *pending, void *data) -{ - struct mt_svc *svc; - struct sbus_conn_ctx *conn_ctx; - DBusMessage *reply; - DBusError dbus_error; - const char *dbus_error_name; - int type; - - svc = talloc_get_type(data, struct mt_svc); - conn_ctx = svc->mt_conn->conn_ctx; - dbus_error_init(&dbus_error); - - reply = dbus_pending_call_steal_reply(pending); - if (!reply) { - /* reply should never be null. This function shouldn't be called - * until reply is valid or timeout has occurred. If reply is NULL - * here, something is seriously wrong and we should bail out. - */ - DEBUG(0, ("A reply callback was called but no reply was received" - " and no timeout occurred\n")); - - /* Destroy this connection */ - sbus_disconnect(conn_ctx); - goto done; - } - - type = dbus_message_get_type(reply); - switch (type) { - case DBUS_MESSAGE_TYPE_METHOD_RETURN: - /* ok peer replied, - * set the reply timestamp into the service structure */ - - DEBUG(4,("Service %s replied to ping\n", svc->name)); - - svc->last_pong = time(NULL); - break; - - case DBUS_MESSAGE_TYPE_ERROR: - - dbus_error_name = dbus_message_get_error_name(reply); - - /* timeouts are handled in the main service check function */ - if (strcmp(dbus_error_name, DBUS_ERROR_TIMEOUT) == 0) - break; - - DEBUG(0,("A service PING returned an error [%s], closing connection.\n", - dbus_error_name)); - /* Falling through to default intentionally*/ - default: - /* - * Timeout or other error occurred or something - * unexpected happened. - * It doesn't matter which, because either way we - * know that this connection isn't trustworthy. - * We'll destroy it now. - */ - sbus_disconnect(conn_ctx); - } - -done: - dbus_pending_call_unref(pending); - dbus_message_unref(reply); -} - -/* service_check_alive - * This function checks if the service child is still alive - */ -static int service_check_alive(struct mt_svc *svc) -{ - int status; - pid_t pid; - - DEBUG(4,("Checking service %s(%d) is still alive\n", svc->name, svc->pid)); - - pid = waitpid(svc->pid, &status, WNOHANG); - if (pid == 0) { - return EOK; - } - - if (pid != svc->pid) { - DEBUG(1, ("bad return (%d) from waitpid() waiting for %d\n", - pid, svc->pid)); - /* TODO: what do we do now ? */ - return EINVAL; - } - - if (WIFEXITED(status)) { /* children exited on it's own */ - /* TODO: check configuration to see if it was removed - * from the list of process to run */ - DEBUG(0,("Process [%s] exited\n", svc->name)); - } - - return ECHILD; -} - -static void free_args(char **args) -{ - int i; - - if (args) { - for (i = 0; args[i]; i++) free(args[i]); - free(args); - } -} - - -/* parse a string into arguments. - * arguments are separated by a space - * '\' is an escape character and can be used only to escape - * itself or the white space. - */ -static char **parse_args(const char *str) -{ - const char *p; - char **ret, **r; - char *tmp; - int num; - int i, e; - - tmp = malloc(strlen(str) + 1); - if (!tmp) return NULL; - - ret = NULL; - num = 0; - e = 0; - i = 0; - p = str; - while (*p) { - switch (*p) { - case '\\': - if (e) { - tmp[i] = '\\'; - i++; - e = 0; - } else { - e = 1; - } - break; - case ' ': - if (e) { - tmp[i] = ' '; - i++; - e = 0; - } else { - tmp[i] = '\0'; - i++; - } - break; - default: - if (e) { - tmp[i] = '\\'; - i++; - e = 0; - } - tmp[i] = *p; - i++; - break; - } - - p++; - - /* check if this was the last char */ - if (*p == '\0') { - if (e) { - tmp[i] = '\\'; - i++; - e = 0; - } - tmp[i] = '\0'; - i++; - } - if (tmp[i-1] != '\0' || strlen(tmp) == 0) { - /* check next char and skip multiple spaces */ - continue; - } - - r = realloc(ret, (num + 2) * sizeof(char *)); - if (!r) goto fail; - ret = r; - ret[num+1] = NULL; - ret[num] = strdup(tmp); - if (!ret[num]) goto fail; - num++; - i = 0; - } - - free(tmp); - return ret; - -fail: - free(tmp); - free_args(ret); - return NULL; -} - -static void service_startup_handler(struct event_context *ev, - struct timed_event *te, - struct timeval t, void *ptr); - -static int start_service(struct mt_svc *svc) -{ - struct timed_event *te; - struct timeval tv; - - DEBUG(4,("Queueing service %s for startup\n", svc->name)); - - /* Add a timed event to start up the service. - * We have to do this in order to avoid a race - * condition where the service being started forks - * and attempts to connect to the SBUS before - * the monitor is serving it. - */ - gettimeofday(&tv, NULL); - te = event_add_timed(svc->mt_ctx->ev, svc, tv, - service_startup_handler, svc); - if (te == NULL) { - DEBUG(0, ("Unable to queue service %s for startup\n", svc->name)); - return ENOMEM; - } - return EOK; -} - -static void service_startup_handler(struct event_context *ev, - struct timed_event *te, - struct timeval t, void *ptr) -{ - struct mt_svc *mt_svc; - char **args; - - mt_svc = talloc_get_type(ptr, struct mt_svc); - if (mt_svc == NULL) { - return; - } - - mt_svc->pid = fork(); - if (mt_svc->pid != 0) { - if (mt_svc->pid == -1) { - DEBUG(0, ("Could not fork child to start service [%s]. Continuing.\n", mt_svc->name)) - return; - } - - /* Parent */ - mt_svc->last_pong = time(NULL); - DLIST_ADD(mt_svc->mt_ctx->svc_list, mt_svc); - set_tasks_checker(mt_svc); - - return; - } - - /* child */ - - args = parse_args(mt_svc->command); - execvp(args[0], args); - - /* If we are here, exec() has failed - * Print errno and abort quickly */ - DEBUG(0,("Could not exec %s, reason: %s\n", mt_svc->command, strerror(errno))); - - /* We have to call _exit() instead of exit() here - * because a bug in D-BUS will cause the server to - * close its socket at exit() */ - _exit(1); -} - -int main(int argc, const char *argv[]) -{ - int opt; - poptContext pc; - int opt_daemon = 0; - int opt_interactive = 0; - int flags = 0; - struct main_context *main_ctx; - int ret; - - struct poptOption long_options[] = { - POPT_AUTOHELP - SSSD_MAIN_OPTS - {"daemon", 'D', POPT_ARG_NONE, &opt_daemon, 0, \ - "Become a daemon (default)", NULL }, \ - {"interactive", 'i', POPT_ARG_NONE, &opt_interactive, 0, \ - "Run interactive (not a daemon)", NULL}, \ - { NULL } - }; - - pc = poptGetContext(argv[0], argc, argv, long_options, 0); - while((opt = poptGetNextOpt(pc)) != -1) { - switch(opt) { - default: - fprintf(stderr, "\nInvalid option %s: %s\n\n", - poptBadOption(pc, 0), poptStrerror(opt)); - poptPrintUsage(pc, stderr, 0); - return 1; - } - } - - if (opt_daemon && opt_interactive) { - fprintf(stderr, "Option -i|--interactive is not allowed together with -D|--daemon\n"); - poptPrintUsage(pc, stderr, 0); - return 1; - } - - poptFreeContext(pc); - - if (opt_daemon) flags |= FLAGS_DAEMON; - if (opt_interactive) flags |= FLAGS_INTERACTIVE; - - /* we want a pid file check */ - flags |= FLAGS_PID_FILE; - - /* set up things like debug , signals, daemonization, etc... */ - ret = server_setup("sssd", flags, &main_ctx); - if (ret != EOK) return 2; - - ret = monitor_process_init(main_ctx, - main_ctx->event_ctx, - main_ctx->confdb_ctx); - if (ret != EOK) return 3; - - /* loop on main */ - server_loop(main_ctx); - - return 0; -} - - diff --git a/server/monitor.h b/server/monitor.h deleted file mode 100644 index 8899c51a..00000000 --- a/server/monitor.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - SSSD - - Service monitor - - Copyright (C) Simo Sorce 2008 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef _MONITOR_H_ -#define _MONITOR_H_ - -int monitor_process_init(TALLOC_CTX *mem_ctx, - struct event_context *event_ctx, - struct confdb_ctx *cdb); - -#endif /* _MONITOR_H */ diff --git a/server/monitor/monitor.c b/server/monitor/monitor.c new file mode 100644 index 00000000..75742eab --- /dev/null +++ b/server/monitor/monitor.c @@ -0,0 +1,1005 @@ +/* + SSSD + + Service monitor + + Copyright (C) Simo Sorce 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include "popt.h" +#include "tevent.h" +#include "util/util.h" +#include "confdb/confdb.h" +#include "monitor/monitor.h" +#include "dbus/dbus.h" +#include "sbus/sssd_dbus.h" +#include "monitor/monitor_interfaces.h" + +/* ping time cannot be less then once every few seconds or the + * monitor will get crazy hammering children with messages */ +#define MONITOR_DEF_PING_TIME 10 + +struct mt_conn { + struct sbus_conn_ctx *conn_ctx; + struct mt_svc *svc_ptr; +}; + +struct mt_svc { + struct mt_svc *prev; + struct mt_svc *next; + + struct mt_conn *mt_conn; + struct mt_ctx *mt_ctx; + + char *command; + char *name; + pid_t pid; + + int ping_time; + + int restarts; + time_t last_restart; + time_t last_pong; +}; + +struct mt_ctx { + struct event_context *ev; + struct confdb_ctx *cdb; + char **services; + struct mt_svc *svc_list; + struct sbus_srv_ctx *sbus_srv; + + int service_id_timeout; +}; + +static int start_service(struct mt_svc *mt_svc); + +static int dbus_service_init(struct sbus_conn_ctx *conn_ctx, void *data); +static void identity_check(DBusPendingCall *pending, void *data); + +static int service_send_ping(struct mt_svc *svc); +static void ping_check(DBusPendingCall *pending, void *data); + +static int service_check_alive(struct mt_svc *svc); + +static void set_tasks_checker(struct mt_svc *srv); + +/* dbus_get_monitor_version + * Return the monitor version over D-BUS */ +static int dbus_get_monitor_version(DBusMessage *message, + void *data, + DBusMessage **r) +{ + const char *version = MONITOR_VERSION; + DBusMessage *reply; + dbus_bool_t ret; + + reply = dbus_message_new_method_return(message); + ret = dbus_message_append_args(reply, DBUS_TYPE_STRING, + &version, DBUS_TYPE_INVALID); + + if (!ret) { + return EIO; + } + + *r = reply; + return EOK; +} + +struct sbus_method monitor_methods[] = { + { MONITOR_METHOD_VERSION, dbus_get_monitor_version}, + {NULL, NULL} +}; + +/* monitor_dbus_init + * Set up the monitor service as a D-BUS Server */ +static int monitor_dbus_init(struct mt_ctx *ctx) +{ + struct sbus_method_ctx *sd_ctx; + struct sbus_srv_ctx *sbus_srv; + char *sbus_address; + char *default_monitor_address; + int ret; + + default_monitor_address = talloc_asprintf(ctx, "unix:path=%s/%s", + PIPE_PATH, SSSD_SERVICE_PIPE); + if (!default_monitor_address) { + return ENOMEM; + } + + ret = confdb_get_string(ctx->cdb, ctx, + "config/services/monitor", "sbusAddress", + default_monitor_address, &sbus_address); + if (ret != EOK) { + talloc_free(default_monitor_address); + return ret; + } + talloc_free(default_monitor_address); + + sd_ctx = talloc_zero(ctx, struct sbus_method_ctx); + if (!sd_ctx) { + talloc_free(sbus_address); + return ENOMEM; + } + + /* Set up globally-available D-BUS methods */ + sd_ctx->interface = talloc_strdup(sd_ctx, MONITOR_DBUS_INTERFACE); + if (!sd_ctx->interface) { + talloc_free(sbus_address); + talloc_free(sd_ctx); + return ENOMEM; + } + sd_ctx->path = talloc_strdup(sd_ctx, MONITOR_DBUS_PATH); + if (!sd_ctx->path) { + talloc_free(sbus_address); + talloc_free(sd_ctx); + return ENOMEM; + } + sd_ctx->methods = monitor_methods; + sd_ctx->message_handler = sbus_message_handler; + + ret = sbus_new_server(ctx, ctx->ev, sd_ctx, &sbus_srv, sbus_address, dbus_service_init, ctx); + ctx->sbus_srv = sbus_srv; + + return ret; +} + +static void tasks_check_handler(struct event_context *ev, + struct timed_event *te, + struct timeval t, void *ptr) +{ + struct mt_svc *svc = talloc_get_type(ptr, struct mt_svc); + time_t now = time(NULL); + bool process_alive = true; + int ret; + + ret = service_check_alive(svc); + switch (ret) { + case EOK: + /* all fine */ + break; + + case ECHILD: + DEBUG(1,("Process (%s) is stopped!\n", svc->name)); + process_alive = false; + break; + + default: + /* TODO: should we tear down it ? */ + DEBUG(1,("Checking for service %s(%d) failed!!\n", + svc->name, svc->pid)); + break; + } + + if (process_alive) { + ret = service_send_ping(svc); + switch (ret) { + case EOK: + /* all fine */ + break; + + case ENXIO: + DEBUG(1,("Child (%s) not responding! (yet)\n", svc->name)); + break; + + default: + /* TODO: should we tear it down ? */ + DEBUG(1,("Sending a message to service (%s) failed!!\n", svc->name)); + break; + } + + if (svc->last_pong != 0) { + if ((now - svc->last_pong) > 30) { /* TODO: get val from config */ + /* too long since we last heard of this process */ + ret = kill(svc->pid, SIGUSR1); + if (ret != EOK) { + DEBUG(0,("Sending signal to child (%s:%d) failed! " + "Ignore and pretend child is dead.\n", + svc->name, svc->pid)); + } + process_alive = false; + } + } + + } + + if (!process_alive) { + DLIST_REMOVE(svc->mt_ctx->svc_list, svc); + if (svc->last_restart != 0) { + if ((now - svc->last_restart) > 30) { /* TODO: get val from config */ + /* it was long ago reset restart threshold */ + svc->restarts = 0; + } + } + + /* restart the process */ + if (svc->restarts > 3) { /* TODO: get val from config */ + DEBUG(0, ("Process [%s], definitely stopped!\n", svc->name)); + talloc_free(svc); + return; + } + + ret = start_service(svc); + if (ret != EOK) { + DEBUG(0,("Failed to restart service '%s'\n", svc->name)); + talloc_free(svc); + return; + } + + svc->restarts++; + svc->last_restart = now; + return; + } + + /* all fine, set up the task checker again */ + set_tasks_checker(svc); +} + +static void set_tasks_checker(struct mt_svc *svc) +{ + struct timed_event *te = NULL; + struct timeval tv; + + gettimeofday(&tv, NULL); + tv.tv_sec += svc->ping_time; + tv.tv_usec = 0; + te = event_add_timed(svc->mt_ctx->ev, svc, tv, tasks_check_handler, svc); + if (te == NULL) { + DEBUG(0, ("failed to add event, monitor offline for [%s]!\n", + svc->name)); + /* FIXME: shutdown ? */ + } +} + +int get_monitor_config(struct mt_ctx *ctx) +{ + int ret; + + ret = confdb_get_int(ctx->cdb, ctx, + "config/services/monitor", "sbusTimeout", + -1, &ctx->service_id_timeout); + if (ret != EOK) { + return ret; + } + + ret = confdb_get_param(ctx->cdb, ctx, + "config/services", "activeServices", + &ctx->services); + + if (ctx->services[0] == NULL) { + DEBUG(0, ("No services configured!\n")); + return EINVAL; + } + + return EOK; +} + +int monitor_process_init(TALLOC_CTX *mem_ctx, + struct event_context *event_ctx, + struct confdb_ctx *cdb) +{ + struct mt_ctx *ctx; + struct mt_svc *svc; + char **doms; + char *path; + int ret, i; + + ctx = talloc_zero(mem_ctx, struct mt_ctx); + if (!ctx) { + DEBUG(0, ("fatal error initializing monitor!\n")); + return ENOMEM; + } + ctx->ev = event_ctx; + ctx->cdb = cdb; + + ret = get_monitor_config(ctx); + if (ret != EOK) + return ret; + + /* Initialize D-BUS Server + * The monitor will act as a D-BUS server for all + * SSSD processes */ + ret = monitor_dbus_init(ctx); + if (ret != EOK) { + return ret; + } + + /* start all services */ + for (i = 0; ctx->services[i]; i++) { + + svc = talloc_zero(ctx, struct mt_svc); + if (!svc) { + talloc_free(ctx); + return ENOMEM; + } + svc->name = ctx->services[i]; + svc->mt_ctx = ctx; + + path = talloc_asprintf(svc, "config/services/%s", svc->name); + if (!path) { + talloc_free(ctx); + return ENOMEM; + } + + ret = confdb_get_string(cdb, svc, path, "command", NULL, &svc->command); + if (ret != EOK) { + DEBUG(0,("Failed to start service '%s'\n", svc->name)); + talloc_free(svc); + continue; + } + + ret = confdb_get_int(cdb, svc, path, "timeout", + MONITOR_DEF_PING_TIME, &svc->ping_time); + if (ret != EOK) { + DEBUG(0,("Failed to start service '%s'\n", svc->name)); + talloc_free(svc); + continue; + } + + talloc_free(path); + + /* Add this service to the queue to be started once the monitor + * enters its mainloop. + */ + ret = start_service(svc); + if (ret != EOK) { + DEBUG(0,("Failed to start service '%s'\n", svc->name)); + talloc_free(svc); + continue; + } + } + + /* now start the data providers */ + ret = confdb_get_domains(cdb, ctx, &doms); + if (ret != EOK) { + DEBUG(2, ("No domains configured. LOCAL should always exist!\n")); + return ret; + } + + for (i = 0; doms[i]; i++) { + svc = talloc_zero(ctx, struct mt_svc); + if (!svc) { + talloc_free(ctx); + return ENOMEM; + } + svc->name = talloc_asprintf(svc, "%%BE_%s", doms[i]); + svc->mt_ctx = ctx; + + path = talloc_asprintf(svc, "config/domains/%s", doms[i]); + if (!path) { + talloc_free(ctx); + return ENOMEM; + } + ret = confdb_get_string(cdb, svc, path, + "command", NULL, &svc->command); + if (ret != EOK) { + DEBUG(0, ("Failed to find provider [%s] configuration\n", doms[i])); + talloc_free(svc); + continue; + } + + ret = confdb_get_int(cdb, svc, path, "timeout", + MONITOR_DEF_PING_TIME, &svc->ping_time); + if (ret != EOK) { + DEBUG(0,("Failed to start service '%s'\n", svc->name)); + talloc_free(svc); + continue; + } + + talloc_free(path); + + /* if no command is present do not run the domain */ + if (svc->command == NULL) { + /* the LOCAL domain does not need a backend at the moment */ + if (strcasecmp(doms[i], "LOCAL") != 0) { + DEBUG(0, ("Missing command to run provider\n")); + } + talloc_free(svc); + continue; + } + + ret = start_service(svc); + if (ret != EOK) { + DEBUG(0,("Failed to start provider for '%s'\n", doms[i])); + talloc_free(svc); + continue; + } + + DLIST_ADD(ctx->svc_list, svc); + + set_tasks_checker(svc); + } + + return EOK; +} + +static int mt_conn_destructor(void *ptr) +{ + struct mt_conn *mt_conn; + struct mt_svc *svc; + + mt_conn = talloc_get_type(ptr, struct mt_conn); + svc = mt_conn->svc_ptr; + + /* now clear up so that the rest of the code will know there + * is no connection attached to the service anymore */ + svc->mt_conn = NULL; + + return 0; +} + +/* + * dbus_service_init + * This function should initiate a query to the newly connected + * service to discover the service's identity (invoke the getIdentity + * method on the new client). The reply callback for this request + * should set the connection destructor appropriately. + */ +static int dbus_service_init(struct sbus_conn_ctx *conn_ctx, void *data) +{ + struct mt_ctx *ctx; + struct mt_svc *svc; + struct mt_conn *mt_conn; + DBusMessage *msg; + DBusPendingCall *pending_reply; + DBusConnection *conn; + DBusError dbus_error; + dbus_bool_t dbret; + + DEBUG(3, ("Initializing D-BUS Service\n")); + + ctx = talloc_get_type(data, struct mt_ctx); + conn = sbus_get_connection(conn_ctx); + dbus_error_init(&dbus_error); + + /* hang off this memory to the connection so that when the connection + * is freed we can call a destructor to clear up the structure and + * have a way to know we need to restart the service */ + mt_conn = talloc(conn_ctx, struct mt_conn); + if (!mt_conn) { + DEBUG(0,("Out of memory?!\n")); + talloc_free(conn_ctx); + return ENOMEM; + } + mt_conn->conn_ctx = conn_ctx; + + /* at this stage we still do not know what service is this + * we will know only after we get its identity, so we make + * up a temporary fake service and complete the operation + * when we receive the reply */ + svc = talloc_zero(mt_conn, struct mt_svc); + if (!svc) { + talloc_free(conn_ctx); + return ENOMEM; + } + svc->mt_ctx = ctx; + svc->mt_conn = mt_conn; + + mt_conn->svc_ptr = svc; + talloc_set_destructor((TALLOC_CTX *)mt_conn, mt_conn_destructor); + + /* + * Set up identity request + * This should be a well-known path and method + * for all services + */ + msg = dbus_message_new_method_call(NULL, + SERVICE_PATH, + SERVICE_INTERFACE, + SERVICE_METHOD_IDENTITY); + if (msg == NULL) { + DEBUG(0,("Out of memory?!\n")); + talloc_free(conn_ctx); + return ENOMEM; + } + dbret = dbus_connection_send_with_reply(conn, msg, &pending_reply, + ctx->service_id_timeout); + if (!dbret) { + /* + * Critical Failure + * We can't communicate on this connection + * We'll drop it using the default destructor. + */ + DEBUG(0, ("D-BUS send failed.\n")); + dbus_message_unref(msg); + talloc_free(conn_ctx); + return EIO; + } + + /* Set up the reply handler */ + dbus_pending_call_set_notify(pending_reply, identity_check, svc, NULL); + dbus_message_unref(msg); + + return EOK; +} + +static void identity_check(DBusPendingCall *pending, void *data) +{ + struct mt_svc *fake_svc; + struct mt_svc *svc; + struct sbus_conn_ctx *conn_ctx; + DBusMessage *reply; + DBusError dbus_error; + dbus_uint16_t svc_ver; + char *svc_name; + dbus_bool_t ret; + int type; + + fake_svc = talloc_get_type(data, struct mt_svc); + conn_ctx = fake_svc->mt_conn->conn_ctx; + dbus_error_init(&dbus_error); + + reply = dbus_pending_call_steal_reply(pending); + if (!reply) { + /* reply should never be null. This function shouldn't be called + * until reply is valid or timeout has occurred. If reply is NULL + * here, something is seriously wrong and we should bail out. + */ + DEBUG(0, ("Serious error. A reply callback was called but no reply was received and no timeout occurred\n")); + + /* Destroy this connection */ + sbus_disconnect(conn_ctx); + goto done; + } + + type = dbus_message_get_type(reply); + switch (type) { + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + ret = dbus_message_get_args(reply, &dbus_error, + DBUS_TYPE_STRING, &svc_name, + DBUS_TYPE_UINT16, &svc_ver, + DBUS_TYPE_INVALID); + if (!ret) { + DEBUG(1,("Failed, to parse message, killing connection\n")); + sbus_disconnect(conn_ctx); + goto done; + } + + /* search this service in the list */ + svc = fake_svc->mt_ctx->svc_list; + while (svc) { + ret = strcasecmp(svc->name, svc_name); + if (ret == 0) { + break; + } + svc = svc->next; + } + if (!svc) { + DEBUG(0,("Unable to find peer in list of services, killing connection!\n")); + sbus_disconnect(conn_ctx); + goto done; + } + + /* transfer all from the fake service and get rid of it */ + fake_svc->mt_conn->svc_ptr = svc; + svc->mt_conn = fake_svc->mt_conn; + talloc_free(fake_svc); + + /* Set up the destructor for this service */ + break; + + case DBUS_MESSAGE_TYPE_ERROR: + DEBUG(0,("getIdentity returned an error [%s], closing connection.\n", + dbus_message_get_error_name(reply))); + /* Falling through to default intentionally*/ + default: + /* + * Timeout or other error occurred or something + * unexpected happened. + * It doesn't matter which, because either way we + * know that this connection isn't trustworthy. + * We'll destroy it now. + */ + sbus_disconnect(conn_ctx); + return; + } + +done: + dbus_pending_call_unref(pending); + dbus_message_unref(reply); +} + +/* service_send_ping + * this function send a dbus ping to a service. + * It returns EOK if all is fine or ENXIO if the connection is + * not available (either not yet set up or teared down). + * Returns e generic error in other cases. + */ +static int service_send_ping(struct mt_svc *svc) +{ + DBusMessage *msg; + DBusPendingCall *pending_reply; + DBusConnection *conn; + DBusError dbus_error; + dbus_bool_t dbret; + + if (!svc->mt_conn) { + return ENXIO; + } + + DEBUG(4,("Pinging %s\n", svc->name)); + + conn = sbus_get_connection(svc->mt_conn->conn_ctx); + dbus_error_init(&dbus_error); + + /* + * Set up identity request + * This should be a well-known path and method + * for all services + */ + msg = dbus_message_new_method_call(NULL, + SERVICE_PATH, + SERVICE_INTERFACE, + SERVICE_METHOD_PING); + if (!msg) { + DEBUG(0,("Out of memory?!\n")); + talloc_free(svc->mt_conn->conn_ctx); + return ENOMEM; + } + + dbret = dbus_connection_send_with_reply(conn, msg, &pending_reply, + svc->mt_ctx->service_id_timeout); + if (!dbret) { + /* + * Critical Failure + * We can't communicate on this connection + * We'll drop it using the default destructor. + */ + DEBUG(0, ("D-BUS send failed.\n")); + talloc_free(svc->mt_conn->conn_ctx); + return EIO; + } + + /* Set up the reply handler */ + dbus_pending_call_set_notify(pending_reply, ping_check, svc, NULL); + dbus_message_unref(msg); + + return EOK; +} + +static void ping_check(DBusPendingCall *pending, void *data) +{ + struct mt_svc *svc; + struct sbus_conn_ctx *conn_ctx; + DBusMessage *reply; + DBusError dbus_error; + const char *dbus_error_name; + int type; + + svc = talloc_get_type(data, struct mt_svc); + conn_ctx = svc->mt_conn->conn_ctx; + dbus_error_init(&dbus_error); + + reply = dbus_pending_call_steal_reply(pending); + if (!reply) { + /* reply should never be null. This function shouldn't be called + * until reply is valid or timeout has occurred. If reply is NULL + * here, something is seriously wrong and we should bail out. + */ + DEBUG(0, ("A reply callback was called but no reply was received" + " and no timeout occurred\n")); + + /* Destroy this connection */ + sbus_disconnect(conn_ctx); + goto done; + } + + type = dbus_message_get_type(reply); + switch (type) { + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + /* ok peer replied, + * set the reply timestamp into the service structure */ + + DEBUG(4,("Service %s replied to ping\n", svc->name)); + + svc->last_pong = time(NULL); + break; + + case DBUS_MESSAGE_TYPE_ERROR: + + dbus_error_name = dbus_message_get_error_name(reply); + + /* timeouts are handled in the main service check function */ + if (strcmp(dbus_error_name, DBUS_ERROR_TIMEOUT) == 0) + break; + + DEBUG(0,("A service PING returned an error [%s], closing connection.\n", + dbus_error_name)); + /* Falling through to default intentionally*/ + default: + /* + * Timeout or other error occurred or something + * unexpected happened. + * It doesn't matter which, because either way we + * know that this connection isn't trustworthy. + * We'll destroy it now. + */ + sbus_disconnect(conn_ctx); + } + +done: + dbus_pending_call_unref(pending); + dbus_message_unref(reply); +} + +/* service_check_alive + * This function checks if the service child is still alive + */ +static int service_check_alive(struct mt_svc *svc) +{ + int status; + pid_t pid; + + DEBUG(4,("Checking service %s(%d) is still alive\n", svc->name, svc->pid)); + + pid = waitpid(svc->pid, &status, WNOHANG); + if (pid == 0) { + return EOK; + } + + if (pid != svc->pid) { + DEBUG(1, ("bad return (%d) from waitpid() waiting for %d\n", + pid, svc->pid)); + /* TODO: what do we do now ? */ + return EINVAL; + } + + if (WIFEXITED(status)) { /* children exited on it's own */ + /* TODO: check configuration to see if it was removed + * from the list of process to run */ + DEBUG(0,("Process [%s] exited\n", svc->name)); + } + + return ECHILD; +} + +static void free_args(char **args) +{ + int i; + + if (args) { + for (i = 0; args[i]; i++) free(args[i]); + free(args); + } +} + + +/* parse a string into arguments. + * arguments are separated by a space + * '\' is an escape character and can be used only to escape + * itself or the white space. + */ +static char **parse_args(const char *str) +{ + const char *p; + char **ret, **r; + char *tmp; + int num; + int i, e; + + tmp = malloc(strlen(str) + 1); + if (!tmp) return NULL; + + ret = NULL; + num = 0; + e = 0; + i = 0; + p = str; + while (*p) { + switch (*p) { + case '\\': + if (e) { + tmp[i] = '\\'; + i++; + e = 0; + } else { + e = 1; + } + break; + case ' ': + if (e) { + tmp[i] = ' '; + i++; + e = 0; + } else { + tmp[i] = '\0'; + i++; + } + break; + default: + if (e) { + tmp[i] = '\\'; + i++; + e = 0; + } + tmp[i] = *p; + i++; + break; + } + + p++; + + /* check if this was the last char */ + if (*p == '\0') { + if (e) { + tmp[i] = '\\'; + i++; + e = 0; + } + tmp[i] = '\0'; + i++; + } + if (tmp[i-1] != '\0' || strlen(tmp) == 0) { + /* check next char and skip multiple spaces */ + continue; + } + + r = realloc(ret, (num + 2) * sizeof(char *)); + if (!r) goto fail; + ret = r; + ret[num+1] = NULL; + ret[num] = strdup(tmp); + if (!ret[num]) goto fail; + num++; + i = 0; + } + + free(tmp); + return ret; + +fail: + free(tmp); + free_args(ret); + return NULL; +} + +static void service_startup_handler(struct event_context *ev, + struct timed_event *te, + struct timeval t, void *ptr); + +static int start_service(struct mt_svc *svc) +{ + struct timed_event *te; + struct timeval tv; + + DEBUG(4,("Queueing service %s for startup\n", svc->name)); + + /* Add a timed event to start up the service. + * We have to do this in order to avoid a race + * condition where the service being started forks + * and attempts to connect to the SBUS before + * the monitor is serving it. + */ + gettimeofday(&tv, NULL); + te = event_add_timed(svc->mt_ctx->ev, svc, tv, + service_startup_handler, svc); + if (te == NULL) { + DEBUG(0, ("Unable to queue service %s for startup\n", svc->name)); + return ENOMEM; + } + return EOK; +} + +static void service_startup_handler(struct event_context *ev, + struct timed_event *te, + struct timeval t, void *ptr) +{ + struct mt_svc *mt_svc; + char **args; + + mt_svc = talloc_get_type(ptr, struct mt_svc); + if (mt_svc == NULL) { + return; + } + + mt_svc->pid = fork(); + if (mt_svc->pid != 0) { + if (mt_svc->pid == -1) { + DEBUG(0, ("Could not fork child to start service [%s]. Continuing.\n", mt_svc->name)) + return; + } + + /* Parent */ + mt_svc->last_pong = time(NULL); + DLIST_ADD(mt_svc->mt_ctx->svc_list, mt_svc); + set_tasks_checker(mt_svc); + + return; + } + + /* child */ + + args = parse_args(mt_svc->command); + execvp(args[0], args); + + /* If we are here, exec() has failed + * Print errno and abort quickly */ + DEBUG(0,("Could not exec %s, reason: %s\n", mt_svc->command, strerror(errno))); + + /* We have to call _exit() instead of exit() here + * because a bug in D-BUS will cause the server to + * close its socket at exit() */ + _exit(1); +} + +int main(int argc, const char *argv[]) +{ + int opt; + poptContext pc; + int opt_daemon = 0; + int opt_interactive = 0; + int flags = 0; + struct main_context *main_ctx; + int ret; + + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_MAIN_OPTS + {"daemon", 'D', POPT_ARG_NONE, &opt_daemon, 0, \ + "Become a daemon (default)", NULL }, \ + {"interactive", 'i', POPT_ARG_NONE, &opt_interactive, 0, \ + "Run interactive (not a daemon)", NULL}, \ + { NULL } + }; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + + if (opt_daemon && opt_interactive) { + fprintf(stderr, "Option -i|--interactive is not allowed together with -D|--daemon\n"); + poptPrintUsage(pc, stderr, 0); + return 1; + } + + poptFreeContext(pc); + + if (opt_daemon) flags |= FLAGS_DAEMON; + if (opt_interactive) flags |= FLAGS_INTERACTIVE; + + /* we want a pid file check */ + flags |= FLAGS_PID_FILE; + + /* set up things like debug , signals, daemonization, etc... */ + ret = server_setup("sssd", flags, &main_ctx); + if (ret != EOK) return 2; + + ret = monitor_process_init(main_ctx, + main_ctx->event_ctx, + main_ctx->confdb_ctx); + if (ret != EOK) return 3; + + /* loop on main */ + server_loop(main_ctx); + + return 0; +} + + diff --git a/server/monitor/monitor.h b/server/monitor/monitor.h new file mode 100644 index 00000000..8899c51a --- /dev/null +++ b/server/monitor/monitor.h @@ -0,0 +1,29 @@ +/* + SSSD + + Service monitor + + Copyright (C) Simo Sorce 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _MONITOR_H_ +#define _MONITOR_H_ + +int monitor_process_init(TALLOC_CTX *mem_ctx, + struct event_context *event_ctx, + struct confdb_ctx *cdb); + +#endif /* _MONITOR_H */ diff --git a/server/monitor/monitor_interfaces.h b/server/monitor/monitor_interfaces.h new file mode 100644 index 00000000..1b91c25a --- /dev/null +++ b/server/monitor/monitor_interfaces.h @@ -0,0 +1,42 @@ +/* + SSSD + + Sbus Interfaces + + Copyright (C) Simo Sorce 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/*** Monitor ***/ + +#define MONITOR_VERSION "0.1" +#define MONITOR_DBUS_INTERFACE "org.freeipa.sssd.monitor" +#define MONITOR_DBUS_PATH "/org/freeipa/sssd/monitor" + +/* Monitor Methods */ +#define MONITOR_METHOD_VERSION "getVersion" + + +/*** Services ***/ + +#define SERVICE_PATH "/org/freeipa/sssd/service" +#define SERVICE_INTERFACE "org.freeipa.sssd.service" + +/* Service Methods */ +#define SERVICE_METHOD_IDENTITY "getIdentity" +#define SERVICE_METHOD_PING "ping" +#define SERVICE_METHOD_RELOAD "reloadConfig" + +#define SSSD_SERVICE_PIPE "private/sbus-monitor" diff --git a/server/monitor/monitor_sbus.c b/server/monitor/monitor_sbus.c new file mode 100644 index 00000000..7a9c8b09 --- /dev/null +++ b/server/monitor/monitor_sbus.c @@ -0,0 +1,97 @@ +/* + SSSD + + Data Provider Helpers + + Copyright (C) Stephen Gallagher 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "util/util.h" +#include "confdb/confdb.h" +#include "sbus/sssd_dbus.h" +#include "monitor/monitor_sbus.h" +#include "monitor/monitor_interfaces.h" + +int monitor_get_sbus_address(TALLOC_CTX *mem_ctx, struct confdb_ctx *confdb, char **address) +{ + int ret; + char *default_address; + + *address = NULL; + default_address = talloc_asprintf(mem_ctx, "unix:path=%s/%s", + PIPE_PATH, SSSD_SERVICE_PIPE); + if (default_address == NULL) { + return ENOMEM; + } + + if (confdb == NULL) { + /* If the confdb isn't specified, fall to the default */ + *address = default_address; + talloc_steal(mem_ctx, default_address); + ret = EOK; + goto done; + } + + ret = confdb_get_string(confdb, mem_ctx, + "config/services/monitor", "sbusAddress", + default_address, address); + +done: + talloc_free(default_address); + return ret; +} + +int monitor_init_sbus_methods(TALLOC_CTX *mem_ctx, struct sbus_method *methods, + struct sbus_method_ctx **sm_ctx) +{ + int ret; + TALLOC_CTX *tmp_ctx; + struct sbus_method_ctx *method_ctx; + + tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + method_ctx = talloc_zero(tmp_ctx, struct sbus_method_ctx); + if (method_ctx == NULL) { + ret = ENOMEM; + goto done; + } + + method_ctx->interface = talloc_strdup(method_ctx, SERVICE_INTERFACE); + if (method_ctx->interface == NULL) { + ret = ENOMEM; + goto done; + } + + method_ctx->path = talloc_strdup(method_ctx, SERVICE_PATH); + if (method_ctx->path == NULL) { + ret = ENOMEM; + goto done; + } + + method_ctx->methods = methods; + method_ctx->message_handler = sbus_message_handler; + + *sm_ctx = method_ctx; + talloc_steal(mem_ctx, method_ctx); + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} diff --git a/server/monitor/monitor_sbus.h b/server/monitor/monitor_sbus.h new file mode 100644 index 00000000..5e110ab8 --- /dev/null +++ b/server/monitor/monitor_sbus.h @@ -0,0 +1,30 @@ +/* + SSSD + + Data Provider Helpers + + Copyright (C) Stephen Gallagher 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef MONITOR_SBUS_H_ +#define MONITOR_SBUS_H_ + +int monitor_get_sbus_address(TALLOC_CTX *mem_ctx, struct confdb_ctx *confdb, + char **address); +int monitor_init_sbus_methods(TALLOC_CTX *mem_ctx, struct sbus_method *methods, + struct sbus_method_ctx **sm_ctx); + +#endif /* MONITOR_SBUS_H_ */ diff --git a/server/nss/nsssrv.c b/server/nss/nsssrv.c index 93a897a2..1c42c3c5 100644 --- a/server/nss/nsssrv.c +++ b/server/nss/nsssrv.c @@ -36,10 +36,11 @@ #include "confdb/confdb.h" #include "dbus/dbus.h" #include "sbus/sssd_dbus.h" -#include "sbus_interfaces.h" #include "util/btreemap.h" -#include "util/service_helpers.h" #include "providers/data_provider.h" +#include "monitor/monitor_sbus.h" +#include "monitor/monitor_interfaces.h" +#include "sbus/sbus_client.h" #define SSS_NSS_PIPE_NAME "nss" @@ -276,14 +277,32 @@ static int service_reload(DBusMessage *message, void *data, DBusMessage **r) { static int nss_sbus_init(struct nss_ctx *nctx) { + int ret; + char *sbus_address; struct service_sbus_ctx *ss_ctx; + struct sbus_method_ctx *sm_ctx; /* Set up SBUS connection to the monitor */ - ss_ctx = sssd_service_sbus_init(nctx, nctx->ev, nctx->cdb, - nss_sbus_methods, NULL); - if (ss_ctx == NULL) { - DEBUG(0, ("Could not initialize D-BUS.\n")); - return ENOMEM; + ret = monitor_get_sbus_address(nctx, nctx->cdb, &sbus_address); + if (ret != EOK) { + DEBUG(0, ("Could not locate monitor address.\n")); + return ret; + } + + ret = monitor_init_sbus_methods(nctx, nss_sbus_methods, &sm_ctx); + if (ret != EOK) { + DEBUG(0, ("Could not initialize SBUS methods.\n")); + return ret; + } + + ret = sbus_client_init(nctx, nctx->ev, + sbus_address, sm_ctx, + NULL /* Private Data */, + NULL /* Destructor */, + &ss_ctx); + if (ret != EOK) { + DEBUG(0, ("Failed to connect to monitor services.\n")); + return ret; } /* Set up NSS-specific listeners */ diff --git a/server/nss/nsssrv_dp.c b/server/nss/nsssrv_dp.c index 567f8e9b..d6aba556 100644 --- a/server/nss/nsssrv_dp.c +++ b/server/nss/nsssrv_dp.c @@ -24,6 +24,8 @@ #include "util/util.h" #include "nss/nsssrv.h" #include "providers/data_provider.h" +#include "sbus/sbus_client.h" +#include "providers/dp_sbus.h" struct nss_dp_req { nss_dp_callback_t callback; @@ -310,6 +312,8 @@ static void nss_dp_conn_reconnect(struct nss_dp_pvt_ctx *pvt) struct nss_ctx *nctx; struct timed_event *te; struct timeval tv; + struct sbus_method_ctx *sm_ctx; + char *sbus_address; time_t now; int ret; @@ -327,9 +331,21 @@ static void nss_dp_conn_reconnect(struct nss_dp_pvt_ctx *pvt) nctx = pvt->nctx; - ret = dp_sbus_cli_init(nctx, nctx->ev, nctx->cdb, - pvt->methods, pvt, - nss_dp_conn_destructor, + ret = dp_get_sbus_address(nctx, nctx->cdb, &sbus_address); + if (ret != EOK) { + DEBUG(0, ("Could not locate data provider address.\n")); + return; + } + + ret = dp_init_sbus_methods(nctx, pvt->methods, &sm_ctx); + if (ret != EOK) { + DEBUG(0, ("Could not initialize SBUS methods.\n")); + return; + } + + ret = sbus_client_init(nctx, nctx->ev, + sbus_address, sm_ctx, + pvt, nss_dp_conn_destructor, &nctx->dp_ctx); if (ret != EOK) { DEBUG(4, ("Failed to reconnect [%d(%s)]!\n", ret, strerror(ret))); @@ -379,7 +395,6 @@ int nss_dp_conn_destructor(void *data) int nss_dp_init(struct nss_ctx *nctx) { struct nss_dp_pvt_ctx *pvt; - int ret; pvt = talloc_zero(nctx, struct nss_dp_pvt_ctx); if (!pvt) return ENOMEM; diff --git a/server/providers/data_provider.c b/server/providers/data_provider.c index fd64525e..e361dfc8 100644 --- a/server/providers/data_provider.c +++ b/server/providers/data_provider.c @@ -34,10 +34,11 @@ #include "confdb/confdb.h" #include "dbus/dbus.h" #include "sbus/sssd_dbus.h" -#include "sbus_interfaces.h" #include "util/btreemap.h" #include "data_provider.h" -#include "util/service_helpers.h" +#include "dp_interfaces.h" +#include "monitor/monitor_sbus.h" +#include "monitor/monitor_interfaces.h" struct dp_backend; struct dp_frontend; @@ -156,14 +157,32 @@ static int service_reload(DBusMessage *message, void *data, DBusMessage **r) { static int dp_monitor_init(struct dp_ctx *dpctx) { + int ret; + char *sbus_address; struct service_sbus_ctx *ss_ctx; + struct sbus_method_ctx *sm_ctx; /* Set up SBUS connection to the monitor */ - ss_ctx = sssd_service_sbus_init(dpctx, dpctx->ev, dpctx->cdb, - mon_sbus_methods, NULL); - if (ss_ctx == NULL) { - DEBUG(0, ("Could not initialize D-BUS.\n")); - return ENOMEM; + ret = monitor_get_sbus_address(dpctx, dpctx->cdb, &sbus_address); + if (ret != EOK) { + DEBUG(0, ("Could not locate monitor address.\n")); + return ret; + } + + ret = monitor_init_sbus_methods(dpctx, mon_sbus_methods, &sm_ctx); + if (ret != EOK) { + DEBUG(0, ("Could not initialize SBUS methods.\n")); + return ret; + } + + ret = sbus_client_init(dpctx, dpctx->ev, + sbus_address, sm_ctx, + NULL /* Private Data */, + NULL /* Destructor */, + &ss_ctx); + if (ret != EOK) { + DEBUG(0, ("Failed to connect to monitor services.\n")); + return ret; } /* Set up DP-specific listeners */ diff --git a/server/providers/data_provider.h b/server/providers/data_provider.h index a7311e5e..382b1fb0 100644 --- a/server/providers/data_provider.h +++ b/server/providers/data_provider.h @@ -32,8 +32,8 @@ #include "confdb/confdb.h" #include "dbus/dbus.h" #include "sbus/sssd_dbus.h" -#include "sbus_interfaces.h" -#include "util/service_helpers.h" +#include "sbus/sbus_client.h" +#include "providers/dp_interfaces.h" #define DATA_PROVIDER_VERSION 0x0001 #define DATA_PROVIDER_SERVICE_NAME "dp" diff --git a/server/providers/data_provider_be.c b/server/providers/data_provider_be.c index ba9ea466..a669564b 100644 --- a/server/providers/data_provider_be.c +++ b/server/providers/data_provider_be.c @@ -35,10 +35,11 @@ #include "db/sysdb.h" #include "dbus/dbus.h" #include "sbus/sssd_dbus.h" -#include "sbus_interfaces.h" #include "util/btreemap.h" #include "providers/dp_backend.h" -#include "util/service_helpers.h" +#include "providers/dp_sbus.h" +#include "monitor/monitor_sbus.h" +#include "monitor/monitor_interfaces.h" typedef int (*be_init_fn_t)(TALLOC_CTX *, struct be_mod_ops **, void **); @@ -286,20 +287,35 @@ done: * sbus channel to the monitor daemon */ static int mon_cli_init(struct be_ctx *ctx) { + int ret; + char *sbus_address; struct service_sbus_ctx *ss_ctx; + struct sbus_method_ctx *sm_ctx; - /* Set up SBUS connection to the monitor */ - ss_ctx = sssd_service_sbus_init(ctx, ctx->ev, ctx->cdb, - mon_sbus_methods, NULL); - if (ss_ctx == NULL) { - DEBUG(0, ("Could not initialize D-BUS.\n")); - return ENOMEM; + /* Set up SBUS connection to the monitor */ + ret = monitor_get_sbus_address(ctx, ctx->cdb, &sbus_address); + if (ret != EOK) { + DEBUG(0, ("Could not locate monitor address.\n")); + return ret; } - ctx->ss_ctx = ss_ctx; + ret = monitor_init_sbus_methods(ctx, mon_sbus_methods, &sm_ctx); + if (ret != EOK) { + DEBUG(0, ("Could not initialize SBUS methods.\n")); + return ret; + } - /* attach be context to the connection */ - sbus_conn_set_private_data(ss_ctx->scon_ctx, ctx); + ret = sbus_client_init(ctx, ctx->ev, + sbus_address, sm_ctx, + ctx /* Private Data */, + NULL /* Destructor */, + &ss_ctx); + if (ret != EOK) { + DEBUG(0, ("Failed to connect to monitor services.\n")); + return ret; + } + + ctx->ss_ctx = ss_ctx; return EOK; } @@ -308,6 +324,38 @@ static int mon_cli_init(struct be_ctx *ctx) * sbus channel to the data provider daemon */ static int be_cli_init(struct be_ctx *ctx) { + int ret; + char *sbus_address; + struct service_sbus_ctx *ss_ctx; + struct sbus_method_ctx *sm_ctx; + + /* Set up SBUS connection to the data provider */ + ret = dp_get_sbus_address(ctx, ctx->cdb, &sbus_address); + if (ret != EOK) { + DEBUG(0, ("Could not locate data provider address.\n")); + return ret; + } + + ret = dp_init_sbus_methods(ctx, mon_sbus_methods, &sm_ctx); + if (ret != EOK) { + DEBUG(0, ("Could not initialize SBUS methods.\n")); + return ret; + } + + ret = sbus_client_init(ctx, ctx->ev, + sbus_address, sm_ctx, + ctx /* Private Data */, + NULL /* Destructor */, + &ss_ctx); + if (ret != EOK) { + DEBUG(0, ("Failed to connect to data provider services.\n")); + return ret; + } + + ctx->ss_ctx = ss_ctx; + + return EOK; + return dp_sbus_cli_init(ctx, ctx->ev, ctx->cdb, be_methods, ctx, NULL, &ctx->dp_ctx); diff --git a/server/providers/dp_interfaces.h b/server/providers/dp_interfaces.h new file mode 100644 index 00000000..8d85eb36 --- /dev/null +++ b/server/providers/dp_interfaces.h @@ -0,0 +1,32 @@ +/* + SSSD + + Data Provider Helpers + + Copyright (C) Stephen Gallagher 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef DP_INTERFACES_H_ +#define DP_INTERFACES_H_ + +/* Data Provider */ + +#define DATA_PROVIDER_INTERFACE "org.freeipa.sssd.dataprovider" +#define DATA_PROVIDER_PATH "/org/freeipa/sssd/dataprovider" + +#define DP_METHOD_CHECK_ONLINE "isOnline" + +#endif /* DP_INTERFACES_H_ */ diff --git a/server/providers/dp_sbus.c b/server/providers/dp_sbus.c new file mode 100644 index 00000000..f6fc12ff --- /dev/null +++ b/server/providers/dp_sbus.c @@ -0,0 +1,98 @@ +/* + SSSD + + Data Provider Helpers + + Copyright (C) Stephen Gallagher 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "util/util.h" +#include "confdb/confdb.h" +#include "sbus/sssd_dbus.h" +#include "providers/data_provider.h" +#include "providers/dp_sbus.h" +#include "providers/dp_interfaces.h" + +int dp_get_sbus_address(TALLOC_CTX *mem_ctx, struct confdb_ctx *confdb, char **address) +{ + int ret; + char *default_address; + + *address = NULL; + default_address = talloc_asprintf(mem_ctx, "unix:path=%s/%s", + PIPE_PATH, DATA_PROVIDER_PIPE); + if (default_address == NULL) { + return ENOMEM; + } + + if (confdb == NULL) { + /* If the confdb isn't specified, fall to the default */ + *address = default_address; + talloc_steal(mem_ctx, default_address); + ret = EOK; + goto done; + } + + ret = confdb_get_string(confdb, mem_ctx, + "config/services/dp", "sbusAddress", + default_address, address); + +done: + talloc_free(default_address); + return ret; +} + +int dp_init_sbus_methods(TALLOC_CTX *mem_ctx, struct sbus_method *methods, + struct sbus_method_ctx **sm_ctx) +{ + int ret; + TALLOC_CTX *tmp_ctx; + struct sbus_method_ctx *method_ctx; + + tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + method_ctx = talloc_zero(tmp_ctx, struct sbus_method_ctx); + if (method_ctx == NULL) { + ret = ENOMEM; + goto done; + } + + method_ctx->interface = talloc_strdup(method_ctx, DATA_PROVIDER_INTERFACE); + if (method_ctx->interface == NULL) { + ret = ENOMEM; + goto done; + } + + method_ctx->path = talloc_strdup(method_ctx, DATA_PROVIDER_PATH); + if (method_ctx->path == NULL) { + ret = ENOMEM; + goto done; + } + + method_ctx->methods = methods; + method_ctx->message_handler = sbus_message_handler; + + *sm_ctx = method_ctx; + talloc_steal(mem_ctx, method_ctx); + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} diff --git a/server/providers/dp_sbus.h b/server/providers/dp_sbus.h new file mode 100644 index 00000000..f21001f9 --- /dev/null +++ b/server/providers/dp_sbus.h @@ -0,0 +1,30 @@ +/* + SSSD + + Data Provider Helpers + + Copyright (C) Stephen Gallagher 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef DP_SBUS_H_ +#define DP_SBUS_H_ + +int dp_get_sbus_address(TALLOC_CTX *mem_ctx, struct confdb_ctx *confdb, + char **address); +int dp_init_sbus_methods(TALLOC_CTX *mem_ctx, struct sbus_method *methods, + struct sbus_method_ctx **sm_ctx); + +#endif /* DP_SBUS_H_ */ diff --git a/server/sbus/sbus_client.c b/server/sbus/sbus_client.c new file mode 100644 index 00000000..9b05b22a --- /dev/null +++ b/server/sbus/sbus_client.c @@ -0,0 +1,79 @@ +/* + SSSD + + Data Provider Helpers + + Copyright (C) Stephen Gallagher 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "util/util.h" +#include "talloc.h" +#include "sbus_client.h" + +int sbus_client_init(TALLOC_CTX *mem_ctx, + struct event_context *ev, + const char *server_address, + struct sbus_method_ctx *sm_ctx, + void *conn_pvt_data, + sbus_conn_destructor_fn destructor, + struct service_sbus_ctx **srvs_ctx) +{ + int ret; + TALLOC_CTX *tmp_ctx; + struct service_sbus_ctx *ss_ctx; + + /* Validate input */ + if (server_address == NULL) { + return EINVAL; + } + + tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + ss_ctx = talloc_zero(tmp_ctx, struct service_sbus_ctx); + if (ss_ctx == NULL) { + ret = ENOMEM; + goto done; + } + ss_ctx->ev = ev; + + ret = sbus_new_connection(ss_ctx, ss_ctx->ev, + server_address, &ss_ctx->scon_ctx, + destructor); + if (ret != EOK) goto done; + + ret = sbus_conn_add_method_ctx(ss_ctx->scon_ctx, sm_ctx); + if (ret != EOK) goto done; + ss_ctx->sm_ctx = sm_ctx; + if (talloc_reference(ss_ctx, sm_ctx) == NULL) { + ret = ENOMEM; + goto done; + } + + if(conn_pvt_data) { + sbus_conn_set_private_data(ss_ctx->scon_ctx, conn_pvt_data); + } + + talloc_steal(mem_ctx, ss_ctx); + *srvs_ctx = ss_ctx; + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} diff --git a/server/sbus/sbus_client.h b/server/sbus/sbus_client.h new file mode 100644 index 00000000..ec61cb0f --- /dev/null +++ b/server/sbus/sbus_client.h @@ -0,0 +1,42 @@ +/* + SSSD + + Data Provider Helpers + + Copyright (C) Stephen Gallagher 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef SBUS_CLIENT_H_ +#define SBUS_CLIENT_H_ + +#include "tevent.h" +#include "sbus/sssd_dbus.h" + +struct service_sbus_ctx { + struct event_context *ev; + struct sbus_conn_ctx *scon_ctx; + struct sbus_method_ctx *sm_ctx; +}; + +int sbus_client_init(TALLOC_CTX *mem_ctx, + struct event_context *ev, + const char *server_address, + struct sbus_method_ctx *sm_ctx, + void *conn_pvt_data, + sbus_conn_destructor_fn destructor, + struct service_sbus_ctx **srvs_ctx); + +#endif /* SBUS_CLIENT_H_ */ diff --git a/server/sbus_interfaces.h b/server/sbus_interfaces.h deleted file mode 100644 index 0a3fe7a8..00000000 --- a/server/sbus_interfaces.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - SSSD - - Sbus Interfaces - - Copyright (C) Simo Sorce 2008 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/*** Monitor ***/ - -#define MONITOR_VERSION "0.1" -#define MONITOR_DBUS_INTERFACE "org.freeipa.sssd.monitor" -#define MONITOR_DBUS_PATH "/org/freeipa/sssd/monitor" - -/* Monitor Methods */ -#define MONITOR_METHOD_VERSION "getVersion" - - -/*** Services ***/ - -#define SERVICE_PATH "/org/freeipa/sssd/service" -#define SERVICE_INTERFACE "org.freeipa.sssd.service" - -/* Service Methods */ -#define SERVICE_METHOD_IDENTITY "getIdentity" -#define SERVICE_METHOD_PING "ping" -#define SERVICE_METHOD_RELOAD "reloadConfig" - -#define SSSD_SERVICE_PIPE "private/sbus-monitor" - -/* Data Provider */ - -#define DATA_PROVIDER_INTERFACE "org.freeipa.sssd.dataprovider" -#define DATA_PROVIDER_PATH "/org/freeipa/sssd/dataprovider" - -#define DP_METHOD_CHECK_ONLINE "isOnline" diff --git a/server/server.mk b/server/server.mk index b77a1729..fd05c170 100644 --- a/server/server.mk +++ b/server/server.mk @@ -4,16 +4,17 @@ UTIL_OBJ = \ util/server.o \ util/memory.o \ util/btreemap.o \ - util/service_helpers.o \ + monitor/monitor_sbus.o \ + providers/dp_sbus.o \ sbus/sssd_dbus_common.o \ sbus/sssd_dbus_connection.o \ sbus/sssd_dbus_server.o \ - providers/dp_helpers.o \ + sbus/sbus_client.o \ confdb/confdb.o \ db/sysdb.o SERVER_OBJ = \ - monitor.o + monitor/monitor.o DP_OBJ = \ providers/data_provider.o diff --git a/server/util/service_helpers.c b/server/util/service_helpers.c deleted file mode 100644 index 9f07abd7..00000000 --- a/server/util/service_helpers.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - SSSD - - Service monitor - - Copyright (C) Stephen Gallagher 2008 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - */ - -#include "util/util.h" -#include "talloc.h" -#include "sbus/sssd_dbus.h" -#include "confdb/confdb.h" -#include "service_helpers.h" -#include "sbus_interfaces.h" - -struct event_context; - -/* - * Set up an SBUS connection to the monitor - */ -struct service_sbus_ctx *sssd_service_sbus_init(TALLOC_CTX *mem_ctx, - struct event_context *ev, - struct confdb_ctx *cdb, - struct sbus_method *methods, - sbus_conn_destructor_fn destructor) -{ - struct service_sbus_ctx *ss_ctx; - struct sbus_method_ctx *sm_ctx; - TALLOC_CTX *ctx; - char *sbus_address; - char *default_monitor_address; - DBusConnection *conn; - int ret; - - ctx = talloc_new(mem_ctx); - if (ctx == NULL) goto error; - - ss_ctx = talloc_zero(ctx, struct service_sbus_ctx); - if (ss_ctx == NULL) return NULL; - - default_monitor_address = talloc_asprintf(ctx, "unix:path=%s/%s", - PIPE_PATH, SSSD_SERVICE_PIPE); - if (default_monitor_address == NULL) goto error; - - ret = confdb_get_string(cdb, ctx, - "config/services/monitor", "sbusAddress", - default_monitor_address, &sbus_address); - if (ret != EOK) goto error; - ss_ctx->ev = ev; - - ret = sbus_new_connection(ss_ctx, ss_ctx->ev, - sbus_address, &ss_ctx->scon_ctx, - destructor); - if (ret != EOK) goto error; - - conn = sbus_get_connection(ss_ctx->scon_ctx); - - /* set up handler for service methods */ - sm_ctx = talloc_zero(ss_ctx, struct sbus_method_ctx); - if (sm_ctx == NULL) goto error; - - sm_ctx->interface = talloc_strdup(sm_ctx, SERVICE_INTERFACE); - sm_ctx->path = talloc_strdup(sm_ctx, SERVICE_PATH); - if (!sm_ctx->interface || !sm_ctx->path) goto error; - - /* Set up required monitor methods and handlers */ - sm_ctx->methods = methods; - sm_ctx->message_handler = sbus_message_handler; - sbus_conn_add_method_ctx(ss_ctx->scon_ctx, sm_ctx); - - talloc_steal(mem_ctx,ss_ctx); - talloc_free(ctx); - return ss_ctx; - -error: - talloc_free(ctx); - return NULL; -} diff --git a/server/util/service_helpers.h b/server/util/service_helpers.h deleted file mode 100644 index 05777ea2..00000000 --- a/server/util/service_helpers.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - SSSD - - Service monitor - - Copyright (C) Stephen Gallagher 2008 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - */ - -#ifndef SERVICE_HELPERS_H_ -#define SERVICE_HELPERS_H_ - -struct service_sbus_ctx { - struct event_context *ev; - struct sbus_conn_ctx *scon_ctx; -}; - -/* - * Set up an SBUS connection to the monitor - */ -struct service_sbus_ctx *sssd_service_sbus_init(TALLOC_CTX *mem_ctx, - struct event_context *ev, - struct confdb_ctx *cdb, - struct sbus_method *methods, - sbus_conn_destructor_fn destructor); - -#endif /*SERVICE_HELPERS_H_*/ -- cgit