diff --git a/proxies/conf_proxy.c b/proxies/conf_proxy.c new file mode 100644 index 00000000..fd909136 --- /dev/null +++ b/proxies/conf_proxy.c @@ -0,0 +1,352 @@ +/* -*- Mode: C ; c-basic-offset: 2 -*- */ +/* + * LADI Session Handler (ladish) + * + * Copyright (C) 2010 Nedko Arnaudov + * + ************************************************************************** + * This file contains implementation of code that interfaces ladiconfd through D-Bus + ************************************************************************** + * + * LADI Session Handler 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 2 of the License, or + * (at your option) any later version. + * + * LADI Session Handler 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 LADI Session Handler. If not, see + * or write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* This implementation subscribes for changes of all values. + * It is suboptimal in general because of the increased overhead. + * A smarter implementation will subscribe using filter based on arg0 (key). + * Such smarter implementation seems to not be worth becase of + * the limited key set that ladish uses. + */ + +#include "conf_proxy.h" + +struct pair +{ + struct list_head siblings; + + uint64_t version; + + char * key; + char * value; + size_t value_buffer_size; + + void (* callback)(void * context, const char * key, const char * value); + void * callback_context; +}; + +static struct list_head g_pairs; + +static struct pair * find_pair(const char * key) +{ + struct list_head * node_ptr; + struct pair * pair_ptr; + + list_for_each(node_ptr, &g_pairs) + { + pair_ptr = list_entry(node_ptr, struct pair, siblings); + if (strcmp(pair_ptr->key, key) == 0) + { + return pair_ptr; + } + } + + return NULL; +} + +static void on_value_changed(struct pair * pair_ptr, const char * value, uint64_t version) +{ + size_t len; + + log_info("'%s' <- '%s' (%"PRIu64")", pair_ptr->key, value, version); + + pair_ptr->version = version; + + len = strlen(value); + if (len < pair_ptr->value_buffer_size) + { + memcpy(pair_ptr->value, value, len + 1); + } + else + { + free(pair_ptr->value); + pair_ptr->value = strdup(value); + if (pair_ptr->value == NULL) + { + log_error("strdup(\"%s\") failed for key \"%s\" value", value, pair_ptr->key); + } + } + + if (pair_ptr->callback != NULL) + { + pair_ptr->callback(pair_ptr->callback_context, pair_ptr->key, value); + } +} + +static void on_life_status_changed(bool appeared) +{ + struct list_head * node_ptr; + struct pair * pair_ptr; + + if (appeared) + { + log_info("confd activatation detected."); + } + else + { + log_info("confd deactivatation detected."); + + list_for_each(node_ptr, &g_pairs) + { + pair_ptr = list_entry(node_ptr, struct pair, siblings); + pair_ptr->version = 0; + } + } +} + +static void on_conf_changed(void * context, DBusMessage * message_ptr) +{ + const char * key; + const char * value; + dbus_uint64_t version; + struct pair * pair_ptr; + + if (!dbus_message_get_args( + message_ptr, + &g_dbus_error, + DBUS_TYPE_STRING, &key, + DBUS_TYPE_STRING, &value, + DBUS_TYPE_UINT64, &version, + DBUS_TYPE_INVALID)) + { + log_error("Invalid parameters of \"changed\" signal: %s", g_dbus_error.message); + dbus_error_free(&g_dbus_error); + return; + } + + pair_ptr = find_pair(key); + if (pair_ptr == NULL) + { + /* we dont care about this key */ + return; + } + + if (pair_ptr->version >= version) + { + /* signal for either already known version of the key or a older one */ + return; + } + + if (pair_ptr->value != NULL && strcmp(value, pair_ptr->value) == 0) + { + /* the conf service should not send the signal when value is not changed, + but in case that it does, ignore it. This can happen when confd is restarted */ + return; + } + + on_value_changed(pair_ptr, value, version); +} + +/* this must be static because it is referenced by the + * dbus helper layer when hooks are active */ +static struct dbus_signal_hook g_signal_hooks[] = +{ + {"changed", on_conf_changed}, + {NULL, NULL} +}; + +bool conf_proxy_init(void) +{ + INIT_LIST_HEAD(&g_pairs); + + if (!dbus_register_service_lifetime_hook(g_dbus_connection, CONF_SERVICE_NAME, on_life_status_changed)) + { + log_error("dbus_register_service_lifetime_hook() failed for confd service"); + return false; + } + + if (!dbus_register_object_signal_hooks( + g_dbus_connection, + CONF_SERVICE_NAME, + CONF_OBJECT_PATH, + CONF_IFACE, + NULL, + g_signal_hooks)) + { + dbus_unregister_service_lifetime_hook(g_dbus_connection, CONF_SERVICE_NAME); + log_error("dbus_register_object_signal_hooks() failed for conf interface"); + return false; + } + + return true; +} + +void conf_proxy_uninit(void) +{ + dbus_unregister_object_signal_hooks(g_dbus_connection, CONF_SERVICE_NAME, CONF_OBJECT_PATH, CONF_IFACE); + dbus_unregister_service_lifetime_hook(g_dbus_connection, CONF_SERVICE_NAME); +} + +bool +conf_register( + const char * key, + void (* callback)(void * context, const char * key, const char * value), + void * callback_context) +{ + const char * value; + uint64_t version; + struct pair * pair_ptr; + + pair_ptr = find_pair(key); + if (pair_ptr != NULL) + { + log_error("key '%s' already registered", key); + ASSERT_NO_PASS; + return false; + } + + if (!dbus_call(CONF_SERVICE_NAME, CONF_OBJECT_PATH, CONF_IFACE, "get", "s", &key, "st", &value, &version)) + { + //log_error("conf::get() failed."); + version = 0; + value = NULL; + } + + pair_ptr = malloc(sizeof(struct pair)); + if (pair_ptr == NULL) + { + log_error("malloc() failed to allocate memory for pair struct"); + return false; + } + + pair_ptr->key = strdup(key); + if (pair_ptr->key == NULL) + { + log_error("strdup(\"%s\") failed for key", key); + free(pair_ptr); + return false; + } + + if (value != NULL) + { + pair_ptr->value_buffer_size = strlen(value) + 1; + + pair_ptr->value = malloc(pair_ptr->value_buffer_size); + if (pair_ptr->value == NULL) + { + log_error("malloc(%zu) failed for value \"%s\"", pair_ptr->value_buffer_size, value); + free(pair_ptr->key); + free(pair_ptr); + return false; + } + memcpy(pair_ptr->value, value, pair_ptr->value_buffer_size); + } + else + { + pair_ptr->value = NULL; + pair_ptr->value_buffer_size = 0; + } + + pair_ptr->version = version; + pair_ptr->callback = callback; + pair_ptr->callback_context = callback_context; + + list_add_tail(&pair_ptr->siblings, &g_pairs); + + if (callback != NULL) + { + callback(callback_context, key, value); + } + + return true; +} + +bool conf_set(const char * key, const char * value) +{ + uint64_t version; + struct pair * pair_ptr; + + if (!dbus_call(CONF_SERVICE_NAME, CONF_OBJECT_PATH, CONF_IFACE, "set", "ss", &key, &value, "t", &version)) + { + log_error("conf::set() failed."); + return false; + } + + /* this is not strictly required because if there is a registered pair, + we should catch the "changed" signal for it */ + pair_ptr = find_pair(key); + if (pair_ptr != NULL && strcmp(value, pair_ptr->value) == 0) + { + on_value_changed(pair_ptr, value, version); + } + + return true; +} + +bool conf_get(const char * key, const char ** value_ptr) +{ + struct pair * pair_ptr; + + pair_ptr = find_pair(key); + if (pair_ptr == NULL) + { + ASSERT_NO_PASS; /* call conf_register() first */ + return false; + } + + if (pair_ptr->value == NULL) + { + return false; /* malloc() failed */ + } + + *value_ptr = pair_ptr->value; + return true; +} + +bool conf_string2bool(const char * value) +{ + if (value[0] == 0 || + strcasecmp(value, "false") == 0 || + strcmp(value, "0") == 0) + { + return false; + } + + return true; +} + +const char * conf_bool2string(bool value) +{ + return value ? "true" : "false"; +} + +bool conf_set_bool(const char * key, bool value) +{ + return conf_set(key, conf_bool2string(value)); +} + +bool conf_get_bool(const char * key, bool * value_ptr) +{ + const char * str; + + if (!conf_get(key, &str)) + { + return false; + } + + *value_ptr = conf_string2bool(str); + + return true; +} diff --git a/proxies/conf_proxy.h b/proxies/conf_proxy.h new file mode 100644 index 00000000..a2c65d48 --- /dev/null +++ b/proxies/conf_proxy.h @@ -0,0 +1,50 @@ +/* -*- Mode: C ; c-basic-offset: 2 -*- */ +/* + * LADI Session Handler (ladish) + * + * Copyright (C) 2010 Nedko Arnaudov + * + ************************************************************************** + * This file contains interface to code that interfaces ladiconfd through D-Bus + ************************************************************************** + * + * LADI Session Handler 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 2 of the License, or + * (at your option) any later version. + * + * LADI Session Handler 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 LADI Session Handler. If not, see + * or write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef CONF_PROXY_H__D45503B2_D49C_46BF_86F7_9A531B819B6B__INCLUDED +#define CONF_PROXY_H__D45503B2_D49C_46BF_86F7_9A531B819B6B__INCLUDED + +#include "common.h" + +bool conf_proxy_init(void); +void conf_proxy_uninit(void); + +bool +conf_register( + const char * key, + void (* callback)(void * context, const char * key, const char * value), + void * callback_context); + +bool conf_set(const char * key, const char * value); +bool conf_get(const char * key, const char ** value_ptr); + +bool conf_string2bool(const char * value); +const char * conf_bool2string(bool value); + +bool conf_set_bool(const char * key, bool value); +bool conf_get_bool(const char * key, bool * value_ptr); + +#endif /* #ifndef CONF_PROXY_H__D45503B2_D49C_46BF_86F7_9A531B819B6B__INCLUDED */