[Openais] [PATCH] Add object tracking to the objdb and confdb
Steven Dake
sdake at redhat.com
Mon Aug 4 22:44:04 PDT 2008
Well
What can I say other then really great work.
I wish I wasn't working on this exact same piece of code at the moment
but it is what it is :)
I'll go ahead and merge this now and refix my changes.
Also what are you plans WRT xpathlite? I noticed you added some types
in the confdb but don't see any api exported that uses it.
Regards
-steve
On Tue, 2008-08-05 at 14:00 +1200, angus salkeld wrote:
> Hi
>
> Here is an implementation of object tracking.
>
> I have slightly extended the tracking API already defined to include
> an object create & destroy notification.
>
> Regards
> Angus
>
> ---
> corosync/Makefile | 2 +-
> corosync/exec/apidef.c | 2 +
> corosync/exec/objdb.c | 268 +++++++++++++++++++++++++++++++++++++-
> corosync/exec/objdb.h | 44 ++++++-
> corosync/include/confdb.h | 33 ++++-
> corosync/include/coroapi.h | 49 +++++++-
> corosync/include/ipc_confdb.h | 30 ++++-
> corosync/include/mar_gen.h | 2 +-
> corosync/lib/confdb.c | 148 ++++++++++++++++++---
> corosync/man/corosync-objctl.8 | 10 +-
> corosync/services/confdb.c | 106 ++++++++++++++-
> corosync/test/testconfdb.c | 4 +-
> corosync/tools/corosync-objctl.c | 147 ++++++++++++++++++++-
> 13 files changed, 799 insertions(+), 46 deletions(-)
>
> diff --git a/corosync/Makefile b/corosync/Makefile
> index 17758c7..445fcac 100644
> --- a/corosync/Makefile
> +++ b/corosync/Makefile
> @@ -173,7 +173,7 @@ install: all
> install -m 755 $(builddir)exec/*lcrso $(DESTDIR)$(LCRSODIR)
> install -m 755 $(builddir)services/*lcrso $(DESTDIR)$(LCRSODIR)
> install -m 755 $(builddir)exec/corosync $(DESTDIR)$(SBINDIR)
> - install -m 755 $(builddir)tools/objctl $(DESTDIR)$(SBINDIR)
> + install -m 755 $(builddir)tools/corosync-objctl $(DESTDIR)$(SBINDIR)
> install -m 700 $(builddir)tools/keygen $(DESTDIR)$(SBINDIR)/ais-keygen
>
> if [ ! -f $(DESTDIR)$(ETCDIR)/penais.conf ] ; then \
> diff --git a/corosync/exec/apidef.c b/corosync/exec/apidef.c
> index 8105a51..c04c586 100644
> --- a/corosync/exec/apidef.c
> +++ b/corosync/exec/apidef.c
> @@ -101,6 +101,8 @@ void apidef_init (struct objdb_iface_ver0 *objdb) {
> apidef_corosync_api_v1.object_find_from = objdb->object_find_from;
> apidef_corosync_api_v1.object_iter_from = objdb->object_iter_from;
> apidef_corosync_api_v1.object_key_iter_from = objdb->object_key_iter_from;
> + apidef_corosync_api_v1.object_track_start = objdb->object_track_start;
> + apidef_corosync_api_v1.object_track_stop = objdb->object_track_stop;
> apidef_corosync_api_v1.object_write_config = objdb->object_write_config;
> }
>
> diff --git a/corosync/exec/objdb.c b/corosync/exec/objdb.c
> index 785aff9..a2cc888 100644
> --- a/corosync/exec/objdb.c
> +++ b/corosync/exec/objdb.c
> @@ -50,6 +50,17 @@ struct object_key {
> struct list_head list;
> };
>
> +struct object_tracker {
> + unsigned int object_handle;
> + void * data_pt;
> + object_track_depth_t depth;
> + object_key_change_notify_fn_t key_change_notify_fn;
> + object_create_notify_fn_t object_create_notify_fn;
> + object_destroy_notify_fn_t object_destroy_notify_fn;
> + struct list_head tracker_list;
> + struct list_head object_list;
> +};
> +
> struct object_instance {
> void *object_name;
> int object_name_len;
> @@ -66,6 +77,7 @@ struct object_instance {
> int object_valid_list_entries;
> struct object_key_valid *object_key_valid_list;
> int object_key_valid_list_entries;
> + struct list_head track_head;
> };
>
> struct object_find_instance {
> @@ -76,6 +88,7 @@ struct object_find_instance {
> };
>
> struct objdb_iface_ver0 objdb_iface;
> +struct list_head objdb_trackers_head;
>
> static struct hdb_handle_database object_instance_database = {
> .handle_count = 0,
> @@ -118,6 +131,8 @@ static int objdb_init (void)
> list_init (&instance->key_head);
> list_init (&instance->child_head);
> list_init (&instance->child_list);
> + list_init (&instance->track_head);
> + list_init (&objdb_trackers_head);
>
> hdb_handle_put (&object_instance_database, handle);
> return (0);
> @@ -129,6 +144,153 @@ error_exit:
> return (-1);
> }
>
> +static int _object_notify_deleted_children(struct object_instance *parent_pt)
> +{
> + struct list_head *list;
> + struct list_head *notify_list;
> + int res;
> + struct object_instance *obj_pt = NULL;
> + struct object_tracker * tracker_pt;
> +
> + for (list = parent_pt->child_head.next;
> + list != &parent_pt->child_head; list = list->next) {
> +
> + obj_pt = list_entry(list, struct object_instance,
> + child_list);
> + res = _object_notify_deleted_children(obj_pt);
> + if (res)
> + return res;
> +
> + for (notify_list = obj_pt->track_head.next;
> + notify_list != &obj_pt->track_head;
> + notify_list = notify_list->next) {
> +
> + tracker_pt = list_entry (notify_list, struct object_tracker, object_list);
> +
> + if ((tracker_pt != NULL) &&
> + (tracker_pt->object_destroy_notify_fn != NULL))
> + tracker_pt->object_destroy_notify_fn(parent_pt->object_handle,
> + obj_pt->object_name,
> + obj_pt->object_name_len,
> + tracker_pt->data_pt);
> + }
> + }
> +
> + return 0;
> +}
> +
> +static void object_created_notification(unsigned int object_handle,
> + unsigned int parent_object_handle,
> + void *name_pt, int name_len)
> +{
> + struct list_head * list;
> + struct object_instance * obj_pt;
> + struct object_tracker * tracker_pt;
> + unsigned int obj_handle = object_handle;
> + unsigned int res;
> +
> + do {
> + res = hdb_handle_get (&object_instance_database,
> + obj_handle, (void *)&obj_pt);
> +
> + for (list = obj_pt->track_head.next;
> + list != &obj_pt->track_head; list = list->next) {
> +
> + tracker_pt = list_entry (list, struct object_tracker, object_list);
> +
> + if (((obj_handle == parent_object_handle) ||
> + (tracker_pt->depth == OBJECT_TRACK_DEPTH_RECURSIVE)) &&
> + (tracker_pt->object_create_notify_fn != NULL)) {
> + tracker_pt->object_create_notify_fn(object_handle, parent_object_handle,
> + name_pt, name_len,
> + tracker_pt->data_pt);
> + }
> + }
> +
> + hdb_handle_put (&object_instance_database, obj_handle);
> + obj_handle = obj_pt->parent_handle;
> +
> + } while (obj_pt->object_handle != OBJECT_PARENT_HANDLE);
> +
> +}
> +
> +static void object_pre_deletion_notification(unsigned int object_handle,
> + unsigned int parent_object_handle,
> + void *name_pt, int name_len)
> +{
> + struct list_head * list;
> + struct object_instance * obj_pt;
> + struct object_tracker * tracker_pt;
> + unsigned int obj_handle = object_handle;
> + unsigned int res;
> +
> + do {
> + res = hdb_handle_get (&object_instance_database,
> + obj_handle, (void *)&obj_pt);
> +
> + for (list = obj_pt->track_head.next;
> + list != &obj_pt->track_head; list = list->next) {
> +
> + tracker_pt = list_entry (list, struct object_tracker, object_list);
> +
> + if (((obj_handle == parent_object_handle) ||
> + (tracker_pt->depth == OBJECT_TRACK_DEPTH_RECURSIVE)) &&
> + (tracker_pt->object_destroy_notify_fn != NULL)) {
> + tracker_pt->object_destroy_notify_fn(parent_object_handle,
> + name_pt, name_len,
> + tracker_pt->data_pt);
> + }
> + }
> + /* notify child object listeners */
> + if (obj_handle == object_handle)
> + _object_notify_deleted_children(obj_pt);
> +
> + hdb_handle_put (&object_instance_database, obj_handle);
> + obj_handle = obj_pt->parent_handle;
> +
> + } while (obj_pt->object_handle != OBJECT_PARENT_HANDLE);
> +
> +}
> +
> +static void object_key_changed_notification(unsigned int object_handle,
> + void *name_pt, int name_len,
> + void *value_pt, int value_len,
> + object_change_type_t type)
> +{
> + struct list_head * list;
> + struct object_instance * obj_pt;
> + struct object_instance * owner_pt = NULL;
> + struct object_tracker * tracker_pt;
> + unsigned int obj_handle = object_handle;
> + unsigned int res;
> +
> + do {
> + res = hdb_handle_get (&object_instance_database,
> + obj_handle, (void *)&obj_pt);
> + if (owner_pt == NULL)
> + owner_pt = obj_pt;
> +
> + for (list = obj_pt->track_head.next;
> + list != &obj_pt->track_head; list = list->next) {
> +
> + tracker_pt = list_entry (list, struct object_tracker, object_list);
> +
> + if (((obj_handle == object_handle) ||
> + (tracker_pt->depth == OBJECT_TRACK_DEPTH_RECURSIVE)) &&
> + (tracker_pt->key_change_notify_fn != NULL))
> + tracker_pt->key_change_notify_fn(type, obj_pt->parent_handle, object_handle,
> + owner_pt->object_name, owner_pt->object_name_len,
> + name_pt, name_len,
> + value_pt, value_len,
> + tracker_pt->data_pt);
> + }
> +
> + hdb_handle_put (&object_instance_database, obj_handle);
> + obj_handle = obj_pt->parent_handle;
> +
> + } while (obj_pt->object_handle != OBJECT_PARENT_HANDLE);
> +}
> +
> /*
> * object db create/destroy/set
> */
> @@ -189,6 +351,7 @@ static int object_create (
> list_init (&object_instance->key_head);
> list_init (&object_instance->child_head);
> list_init (&object_instance->child_list);
> + list_init (&object_instance->track_head);
> object_instance->object_name = malloc (object_name_len);
> if (object_instance->object_name == 0) {
> goto error_put_destroy;
> @@ -211,6 +374,10 @@ static int object_create (
> hdb_handle_put (&object_instance_database, *object_handle);
>
> hdb_handle_put (&object_instance_database, parent_object_handle);
> + object_created_notification(object_instance->object_handle,
> + object_instance->parent_handle,
> + object_instance->object_name,
> + object_instance->object_name_len);
>
> return (0);
>
> @@ -322,6 +489,8 @@ static int object_key_create (
>
> list_init (&object_key->list);
> list_add (&object_key->list, &instance->key_head);
> + object_key_changed_notification(object_handle, key_name, key_len,
> + value, value_len, OBJECT_KEY_CREATED);
>
> return (0);
>
> @@ -338,7 +507,6 @@ error_exit:
> return (-1);
> }
>
> -
> static int _clear_object(struct object_instance *instance)
> {
> struct list_head *list;
> @@ -390,6 +558,11 @@ static int object_destroy (
> return (res);
> }
>
> + object_pre_deletion_notification(object_handle,
> + instance->parent_handle,
> + instance->object_name,
> + instance->object_name_len);
> +
> /* Recursively clear sub-objects & keys */
> res = _clear_object(instance);
>
> @@ -641,6 +814,9 @@ static int object_key_delete (
> }
>
> hdb_handle_put (&object_instance_database, object_handle);
> + if (ret == 0)
> + object_key_changed_notification(object_handle, key_name, key_len,
> + value, value_len, OBJECT_KEY_DELETED);
> return (ret);
>
> error_exit:
> @@ -735,6 +911,9 @@ static int object_key_replace (
> }
>
> hdb_handle_put (&object_instance_database, object_handle);
> + if (ret == 0)
> + object_key_changed_notification(object_handle, key_name, key_len,
> + new_value, new_value_len, OBJECT_KEY_REPLACED);
> return (ret);
>
> error_put:
> @@ -1093,8 +1272,6 @@ error_exit:
> }
>
>
> -
> -
> static int object_parent_get(unsigned int object_handle,
> unsigned int *parent_handle)
> {
> @@ -1118,6 +1295,89 @@ static int object_parent_get(unsigned int object_handle,
> }
>
>
> +static int object_track_start(unsigned int object_handle,
> + object_track_depth_t depth,
> + object_key_change_notify_fn_t key_change_notify_fn,
> + object_create_notify_fn_t object_create_notify_fn,
> + object_destroy_notify_fn_t object_destroy_notify_fn,
> + void * priv_data_pt)
> +{
> + struct object_instance *instance;
> + unsigned int res;
> + struct object_tracker * tracker_pt;
> +
> + res = hdb_handle_get (&object_instance_database,
> + object_handle, (void *)&instance);
> + if (res != 0) {
> + return (res);
> + }
> + tracker_pt = malloc(sizeof(struct object_tracker));
> +
> + tracker_pt->depth = depth;
> + tracker_pt->object_handle = object_handle;
> + tracker_pt->key_change_notify_fn = key_change_notify_fn;
> + tracker_pt->object_create_notify_fn = object_create_notify_fn;
> + tracker_pt->object_destroy_notify_fn = object_destroy_notify_fn;
> + tracker_pt->data_pt = priv_data_pt;
> +
> + list_init(&tracker_pt->object_list);
> + list_init(&tracker_pt->tracker_list);
> +
> + list_add(&tracker_pt->object_list, &instance->track_head);
> + list_add(&tracker_pt->tracker_list, &objdb_trackers_head);
> +
> + hdb_handle_put (&object_instance_database, object_handle);
> +
> + return (res);
> +}
> +
> +static void object_track_stop(object_key_change_notify_fn_t key_change_notify_fn,
> + object_create_notify_fn_t object_create_notify_fn,
> + object_destroy_notify_fn_t object_destroy_notify_fn,
> + void * priv_data_pt)
> +{
> + struct object_instance *instance;
> + struct object_tracker * tracker_pt = NULL;
> + struct object_tracker * obj_tracker_pt = NULL;
> + struct list_head *list, *tmp_list;
> + struct list_head *obj_list, *tmp_obj_list;
> + unsigned int res;
> +
> + /* go through the global list and find all the trackers to stop */
> + for (list = objdb_trackers_head.next, tmp_list = list->next;
> + list != &objdb_trackers_head; list = tmp_list, tmp_list = tmp_list->next) {
> +
> + tracker_pt = list_entry (list, struct object_tracker, tracker_list);
> +
> + if (tracker_pt && (tracker_pt->data_pt == priv_data_pt) &&
> + (tracker_pt->object_create_notify_fn == object_create_notify_fn) &&
> + (tracker_pt->object_destroy_notify_fn == object_destroy_notify_fn) &&
> + (tracker_pt->key_change_notify_fn == key_change_notify_fn)) {
> +
> + /* get the object & take this tracker off of it's list. */
> +
> + res = hdb_handle_get (&object_instance_database,
> + tracker_pt->object_handle, (void *)&instance);
> + if (res != 0) continue;
> +
> + for (obj_list = instance->track_head.next, tmp_obj_list = obj_list->next;
> + obj_list != &instance->track_head; obj_list = tmp_obj_list, tmp_obj_list = tmp_obj_list->next) {
> +
> + obj_tracker_pt = list_entry (obj_list, struct object_tracker, object_list);
> + if (obj_tracker_pt == tracker_pt) {
> + /* this is the tracker we are after. */
> + list_del(obj_list);
> + }
> + }
> + hdb_handle_put (&object_instance_database, tracker_pt->object_handle);
> +
> + /* remove the tracker off of the global list */
> + list_del(list);
> + free(tracker_pt);
> + }
> + }
> +}
> +
> static int object_dump(unsigned int object_handle,
> FILE *file)
> {
> @@ -1178,6 +1438,8 @@ struct objdb_iface_ver0 objdb_iface = {
> .object_iter_from = object_iter_from,
> .object_priv_get = object_priv_get,
> .object_parent_get = object_parent_get,
> + .object_track_start = object_track_start,
> + .object_track_stop = object_track_stop,
> .object_dump = object_dump,
> .object_write_config = object_write_config,
> };
> diff --git a/corosync/exec/objdb.h b/corosync/exec/objdb.h
> index 9656eaa..608bd42 100644
> --- a/corosync/exec/objdb.h
> +++ b/corosync/exec/objdb.h
> @@ -40,11 +40,39 @@
>
> #include <stdio.h>
>
> +typedef enum {
> + OBJECT_TRACK_DEPTH_ONE,
> + OBJECT_TRACK_DEPTH_RECURSIVE
> +} object_track_depth_t;
> +
> +typedef enum {
> + OBJECT_KEY_CREATED,
> + OBJECT_KEY_REPLACED,
> + OBJECT_KEY_DELETED
> +} object_change_type_t;
> +
> +typedef void (*object_key_change_notify_fn_t)(object_change_type_t change_type,
> + unsigned int parent_object_handle,
> + unsigned int object_handle,
> + void *object_name_pt, int object_name_len,
> + void *key_name_pt, int key_len,
> + void *key_value_pt, int key_value_len,
> + void *priv_data_pt);
> +
> +typedef void (*object_create_notify_fn_t) (unsigned int parent_object_handle,
> + unsigned int object_handle,
> + void *name_pt, int name_len,
> + void *priv_data_pt);
> +
> +typedef void (*object_destroy_notify_fn_t) (unsigned int parent_object_handle,
> + void *name_pt, int name_len,
> + void *priv_data_pt);
> +
> struct object_valid {
> char *object_name;
> int object_len;
> };
> -
> +
> struct object_key_valid {
> char *key_name;
> int key_len;
> @@ -174,6 +202,20 @@ struct objdb_iface_ver0 {
> void **value,
> int *value_len);
>
> + int (*object_track_start) (
> + unsigned int object_handle,
> + object_track_depth_t depth,
> + object_key_change_notify_fn_t key_change_notify_fn,
> + object_create_notify_fn_t object_create_notify_fn,
> + object_destroy_notify_fn_t object_destroy_notify_fn,
> + void * priv_data_pt);
> +
> + void (*object_track_stop) (
> + object_key_change_notify_fn_t key_change_notify_fn,
> + object_create_notify_fn_t object_create_notify_fn,
> + object_destroy_notify_fn_t object_destroy_notify_fn,
> + void * priv_data_pt);
> +
> int (*object_write_config) (char **error_string);
> };
>
> diff --git a/corosync/include/confdb.h b/corosync/include/confdb.h
> index 831d982..7e1b9ee 100644
> --- a/corosync/include/confdb.h
> +++ b/corosync/include/confdb.h
> @@ -50,6 +50,11 @@ typedef enum {
> } confdb_dispatch_t;
>
> typedef enum {
> + CONFDB_TRACK_DEPTH_ONE,
> + CONFDB_TRACK_DEPTH_RECURSIVE
> +} confdb_track_depth_t;
> +
> +typedef enum {
> CONFDB_OK = 1,
> CONFDB_ERR_LIBRARY = 2,
> CONFDB_ERR_TIMEOUT = 5,
> @@ -65,9 +70,15 @@ typedef enum {
> CONFDB_ERR_SECURITY = 29,
> } confdb_error_t;
>
> +typedef enum {
> + OBJECT_KEY_CREATED,
> + OBJECT_KEY_REPLACED,
> + OBJECT_KEY_DELETED
> +} confdb_change_type_t;
>
> -typedef void (*confdb_change_notify_fn_t) (
> +typedef void (*confdb_key_change_notify_fn_t) (
> confdb_handle_t handle,
> + confdb_change_type_t change_type,
> unsigned int parent_object_handle,
> unsigned int object_handle,
> void *object_name,
> @@ -77,8 +88,23 @@ typedef void (*confdb_change_notify_fn_t) (
> void *key_value,
> int key_value_len);
>
> +typedef void (*confdb_object_create_notify_fn_t) (
> + confdb_handle_t handle,
> + unsigned int parent_object_handle,
> + unsigned int object_handle,
> + uint8_t *name_pt,
> + int name_len);
> +
> +typedef void (*confdb_object_delete_notify_fn_t) (
> + confdb_handle_t handle,
> + unsigned int parent_object_handle,
> + uint8_t *name_pt,
> + int name_len);
> +
> typedef struct {
> - confdb_change_notify_fn_t confdb_change_notify_fn;
> + confdb_object_create_notify_fn_t confdb_object_create_change_notify_fn;
> + confdb_object_delete_notify_fn_t confdb_object_delete_change_notify_fn;
> + confdb_key_change_notify_fn_t confdb_key_change_notify_fn;
> } confdb_callbacks_t;
>
> /** @} */
> @@ -119,7 +145,6 @@ confdb_error_t confdb_dispatch (
> confdb_handle_t handle,
> confdb_dispatch_t dispatch_types);
>
> -
> /*
> * Change notification
> */
> @@ -194,7 +219,7 @@ confdb_error_t confdb_key_replace (
> * Object queries
> * "find" loops through all objects of a given name and is also
> * a quick way of finding a specific object,
> - * "iter" returns ech object in sequence.
> + * "iter" returns each object in sequence.
> */
> confdb_error_t confdb_object_find_start (
> confdb_handle_t handle,
> diff --git a/corosync/include/coroapi.h b/corosync/include/coroapi.h
> index 0cd2335..aa9baa8 100644
> --- a/corosync/include/coroapi.h
> +++ b/corosync/include/coroapi.h
> @@ -90,13 +90,46 @@ struct object_valid {
> char *object_name;
> int object_len;
> };
> -
> +
> struct object_key_valid {
> char *key_name;
> int key_len;
> int (*validate_callback) (void *key, int key_len, void *value, int value_len);
> };
>
> +typedef enum {
> + OBJECT_TRACK_DEPTH_ONE,
> + OBJECT_TRACK_DEPTH_RECURSIVE
> +} object_track_depth_t;
> +
> +typedef enum {
> + OBJECT_KEY_CREATED,
> + OBJECT_KEY_REPLACED,
> + OBJECT_KEY_DELETED
> +} object_change_type_t;
> +
> +typedef void (*object_key_change_notify_fn_t)(object_change_type_t change_type,
> + unsigned int parent_object_handle,
> + unsigned int object_handle,
> + void *object_name_pt, int object_name_len,
> + void *key_name_pt, int key_len,
> + void *key_value_pt, int key_value_len,
> + void *priv_data_pt);
> +
> +typedef void (*object_create_notify_fn_t) (unsigned int parent_object_handle,
> + unsigned int object_handle,
> + uint8_t *name_pt, int name_len,
> + void *priv_data_pt);
> +
> +typedef void (*object_destroy_notify_fn_t) (unsigned int parent_object_handle,
> + uint8_t *name_pt, int name_len,
> + void *priv_data_pt);
> +typedef void (*object_notify_callback_fn_t)(unsigned int object_handle,
> + void *key_name, int key_len,
> + void *value, int value_len,
> + object_change_type_t type,
> + void * priv_data_pt);
> +
> #endif /* OBJECT_PARENT_HANDLE_DEFINED */
>
> struct corosync_api_v1 {
> @@ -222,6 +255,20 @@ struct corosync_api_v1 {
> void **value,
> int *value_len);
>
> + int (*object_track_start) (
> + unsigned int object_handle,
> + object_track_depth_t depth,
> + object_key_change_notify_fn_t key_change_notify_fn,
> + object_create_notify_fn_t object_create_notify_fn,
> + object_destroy_notify_fn_t object_destroy_notify_fn,
> + void * priv_data_pt);
> +
> + void (*object_track_stop) (
> + object_key_change_notify_fn_t key_change_notify_fn,
> + object_create_notify_fn_t object_create_notify_fn,
> + object_destroy_notify_fn_t object_destroy_notify_fn,
> + void * priv_data_pt);
> +
> int (*object_write_config) (char **error_string);
>
> /*
> diff --git a/corosync/include/ipc_confdb.h b/corosync/include/ipc_confdb.h
> index acc0871..ab01a6f 100644
> --- a/corosync/include/ipc_confdb.h
> +++ b/corosync/include/ipc_confdb.h
> @@ -51,7 +51,8 @@ enum req_confdb_types {
> MESSAGE_REQ_CONFDB_KEY_ITER = 9,
> MESSAGE_REQ_CONFDB_TRACK_START = 10,
> MESSAGE_REQ_CONFDB_TRACK_STOP = 11,
> - MESSAGE_REQ_CONFDB_WRITE = 12
> + MESSAGE_REQ_CONFDB_XPATH_EVAL_EXPRESSION = 12,
> + MESSAGE_REQ_CONFDB_WRITE = 13
> };
>
> enum res_confdb_types {
> @@ -67,8 +68,10 @@ enum res_confdb_types {
> MESSAGE_RES_CONFDB_KEY_ITER = 9,
> MESSAGE_RES_CONFDB_TRACK_START = 10,
> MESSAGE_RES_CONFDB_TRACK_STOP = 11,
> - MESSAGE_RES_CONFDB_CHANGE_CALLBACK = 12,
> - MESSAGE_RES_CONFDB_WRITE = 13
> + MESSAGE_RES_CONFDB_KEY_CHANGE_CALLBACK = 12,
> + MESSAGE_RES_CONFDB_OBJECT_CREATE_CALLBACK = 13,
> + MESSAGE_RES_CONFDB_OBJECT_DESTROY_CALLBACK = 14,
> + MESSAGE_RES_CONFDB_WRITE = 15
> };
>
>
> @@ -174,8 +177,9 @@ struct res_lib_confdb_write {
> mar_name_t error __attribute__((aligned(8)));
> };
>
> -struct res_lib_confdb_change_callback {
> +struct res_lib_confdb_key_change_callback {
> mar_res_header_t header __attribute__((aligned(8)));
> + mar_uint32_t change_type __attribute__((aligned(8)));
> mar_uint32_t parent_object_handle __attribute__((aligned(8)));
> mar_uint32_t object_handle __attribute__((aligned(8)));
> mar_name_t object_name __attribute__((aligned(8)));
> @@ -183,5 +187,23 @@ struct res_lib_confdb_change_callback {
> mar_name_t key_value __attribute__((aligned(8)));
> };
>
> +struct res_lib_confdb_object_create_callback {
> + mar_res_header_t header __attribute__((aligned(8)));
> + mar_uint32_t parent_object_handle __attribute__((aligned(8)));
> + mar_uint32_t object_handle __attribute__((aligned(8)));
> + mar_name_t name __attribute__((aligned(8)));
> +};
> +
> +struct res_lib_confdb_object_destroy_callback {
> + mar_res_header_t header __attribute__((aligned(8)));
> + mar_uint32_t parent_object_handle __attribute__((aligned(8)));
> + mar_name_t name __attribute__((aligned(8)));
> +};
> +
> +struct req_lib_confdb_object_track_start {
> + mar_req_header_t header __attribute__((aligned(8)));
> + mar_uint32_t object_handle __attribute__((aligned(8)));
> + mar_uint32_t flags __attribute__((aligned(8)));
> +};
>
> #endif /* IPC_CONFDB_H_DEFINED */
> diff --git a/corosync/include/mar_gen.h b/corosync/include/mar_gen.h
> index 09a839d..af343e5 100644
> --- a/corosync/include/mar_gen.h
> +++ b/corosync/include/mar_gen.h
> @@ -109,7 +109,7 @@ static inline char *get_mar_name_t (mar_name_t *name) {
> return ((char *)name->value);
> }
>
> -static int mar_name_match(mar_name_t *name1, mar_name_t *name2)
> +static inline int mar_name_match(mar_name_t *name1, mar_name_t *name2)
> {
> if (name1->length == name2->length) {
> return ((strncmp ((char *)name1->value, (char *)name2->value,
> diff --git a/corosync/lib/confdb.c b/corosync/lib/confdb.c
> index 7e22c03..8ff8cb2 100644
> --- a/corosync/lib/confdb.c
> +++ b/corosync/lib/confdb.c
> @@ -304,8 +304,10 @@ confdb_error_t confdb_dispatch (
> int cont = 1; /* always continue do loop except when set to 0 */
> int dispatch_avail;
> struct confdb_inst *confdb_inst;
> - struct res_lib_confdb_change_callback *res_confdb_change_callback;
> confdb_callbacks_t callbacks;
> + struct res_lib_confdb_key_change_callback *res_key_changed_pt;
> + struct res_lib_confdb_object_create_callback *res_object_created_pt;
> + struct res_lib_confdb_object_destroy_callback *res_object_destroyed_pt;
> struct res_overlay dispatch_data;
> int ignore_dispatch = 0;
>
> @@ -400,24 +402,44 @@ confdb_error_t confdb_dispatch (
> * Dispatch incoming message
> */
> switch (dispatch_data.header.id) {
> - case MESSAGE_RES_CONFDB_CHANGE_CALLBACK:
> - res_confdb_change_callback = (struct res_lib_confdb_change_callback *)&dispatch_data;
> -
> - callbacks.confdb_change_notify_fn (handle,
> - res_confdb_change_callback->parent_object_handle,
> - res_confdb_change_callback->object_handle,
> - res_confdb_change_callback->object_name.value,
> - res_confdb_change_callback->object_name.length,
> - res_confdb_change_callback->key_name.value,
> - res_confdb_change_callback->key_name.length,
> - res_confdb_change_callback->key_value.value,
> - res_confdb_change_callback->key_value.length);
> - break;
> -
> - default:
> - error = SA_AIS_ERR_LIBRARY;
> - goto error_nounlock;
> - break;
> + case MESSAGE_RES_CONFDB_KEY_CHANGE_CALLBACK:
> + res_key_changed_pt = (struct res_lib_confdb_key_change_callback *)&dispatch_data;
> +
> + callbacks.confdb_key_change_notify_fn(handle,
> + res_key_changed_pt->change_type,
> + res_key_changed_pt->object_handle,
> + res_key_changed_pt->parent_object_handle,
> + res_key_changed_pt->object_name.value,
> + res_key_changed_pt->object_name.length,
> + res_key_changed_pt->key_name.value,
> + res_key_changed_pt->key_name.length,
> + res_key_changed_pt->key_value.value,
> + res_key_changed_pt->key_value.length);
> + break;
> +
> + case MESSAGE_RES_CONFDB_OBJECT_CREATE_CALLBACK:
> + res_object_created_pt = (struct res_lib_confdb_object_create_callback *)&dispatch_data;
> +
> + callbacks.confdb_object_create_change_notify_fn(handle,
> + res_object_created_pt->object_handle,
> + res_object_created_pt->parent_object_handle,
> + res_object_created_pt->name.value,
> + res_object_created_pt->name.length);
> + break;
> +
> + case MESSAGE_RES_CONFDB_OBJECT_DESTROY_CALLBACK:
> + res_object_destroyed_pt = (struct res_lib_confdb_object_destroy_callback *)&dispatch_data;
> +
> + callbacks.confdb_object_delete_change_notify_fn(handle,
> + res_object_destroyed_pt->parent_object_handle,
> + res_object_destroyed_pt->name.value,
> + res_object_destroyed_pt->name.length);
> + break;
> +
> + default:
> + error = SA_AIS_ERR_LIBRARY;
> + goto error_nounlock;
> + break;
> }
>
> /*
> @@ -1197,4 +1219,92 @@ error_exit:
> return (error);
> }
>
> +confdb_error_t confdb_track_changes (
> + confdb_handle_t handle,
> + unsigned int object_handle,
> + unsigned int flags)
> +{
> + confdb_error_t error;
> + struct confdb_inst *confdb_inst;
> + struct iovec iov[2];
> + struct req_lib_confdb_object_track_start req;
> + mar_res_header_t res;
> +
> + error = saHandleInstanceGet (&confdb_handle_t_db, handle, (void *)&confdb_inst);
> + if (error != SA_AIS_OK) {
> + return (error);
> + }
> +
> + if (confdb_inst->standalone) {
> + error = CONFDB_ERR_NOT_SUPPORTED;
> + goto error_exit;
> + }
> +
> + req.header.size = sizeof (struct req_lib_confdb_object_track_start);
> + req.header.id = MESSAGE_REQ_CONFDB_TRACK_START;
> + req.object_handle = object_handle;
> + req.flags = flags;
> +
> + iov[0].iov_base = (char *)&req;
> + iov[0].iov_len = sizeof (struct req_lib_confdb_object_track_start);
> +
> + pthread_mutex_lock (&confdb_inst->response_mutex);
> +
> + error = saSendMsgReceiveReply (confdb_inst->response_fd, iov, 1,
> + &res, sizeof ( mar_res_header_t));
> +
> + pthread_mutex_unlock (&confdb_inst->response_mutex);
> + if (error != SA_AIS_OK) {
> + goto error_exit;
> + }
> +
> + error = res.error;
> +
> +error_exit:
> + saHandleInstancePut (&confdb_handle_t_db, handle);
> +
> + return (error);
> +}
> +
> +confdb_error_t confdb_stop_track_changes (confdb_handle_t handle)
> +{
> + confdb_error_t error;
> + struct confdb_inst *confdb_inst;
> + struct iovec iov[2];
> + mar_req_header_t req;
> + mar_res_header_t res;
> +
> + error = saHandleInstanceGet (&confdb_handle_t_db, handle, (void *)&confdb_inst);
> + if (error != SA_AIS_OK) {
> + return (error);
> + }
> +
> + if (confdb_inst->standalone) {
> + error = CONFDB_ERR_NOT_SUPPORTED;
> + goto error_exit;
> + }
> +
> + req.size = sizeof (mar_req_header_t);
> + req.id = MESSAGE_REQ_CONFDB_TRACK_STOP;
> +
> + iov[0].iov_base = (char *)&req;
> + iov[0].iov_len = sizeof (mar_req_header_t);
> +
> + pthread_mutex_lock (&confdb_inst->response_mutex);
> +
> + error = saSendMsgReceiveReply (confdb_inst->response_fd, iov, 1,
> + &res, sizeof ( mar_res_header_t));
> +
> + pthread_mutex_unlock (&confdb_inst->response_mutex);
> + if (error != SA_AIS_OK) {
> + goto error_exit;
> + }
> +
> + error = res.error;
> +
> +error_exit:
> + saHandleInstancePut (&confdb_handle_t_db, handle);
> +
> + return (error);
> +}
>
> diff --git a/corosync/man/corosync-objctl.8 b/corosync/man/corosync-objctl.8
> index f230008..250a99c 100644
> --- a/corosync/man/corosync-objctl.8
> +++ b/corosync/man/corosync-objctl.8
> @@ -35,7 +35,7 @@
> .SH NAME
> corosync-objctl \- Configure objects in the Object Database
> .SH SYNOPSIS
> -.B "corosync-objctl [\-c|\-w|\-d|\-a|\-h] <OBJECT-SPEC>..."
> +.B "corosync-objctl [\-c|\-w|\-d|\-a|\-t\-h] <OBJECT-SPEC>..."
> .SH DESCRIPTION
> .B corosync-objctl
> is used to configure objects within the object database at runtime.
> @@ -62,12 +62,16 @@ Create a new object.
> .B -d
> Delete an existing object.
> .TP
> -.B "-w"
> +.B -w
> Use this option when you want to write a new value to a key.
> .TP
> -.B "-a"
> +.B -a
> Display all values currently available.
> .TP
> +.B -t
> +Track changes to an object and it's children. As changes are made to the object
> +they are printed out. this is kind of like a "tail -f" for the object database.
> +.TP
> .B -h
> Print basic usage.
> .SH EXAMPLES
> diff --git a/corosync/services/confdb.c b/corosync/services/confdb.c
> index 2b56b00..440e44b 100644
> --- a/corosync/services/confdb.c
> +++ b/corosync/services/confdb.c
> @@ -46,7 +46,7 @@
> #include "../exec/logsys.h"
> #include "../include/coroapi.h"
>
> -LOGSYS_DECLARE_SUBSYS ("CONFDB", LOG_INFO);
> +LOGSYS_DECLARE_SUBSYS ("CONFDB", LOG_DEBUG);
>
> static struct corosync_api_v1 *api;
>
> @@ -74,7 +74,21 @@ static void message_handler_req_lib_confdb_write (void *conn, void *message);
> static void message_handler_req_lib_confdb_track_start (void *conn, void *message);
> static void message_handler_req_lib_confdb_track_stop (void *conn, void *message);
>
> -
> +static void confdb_notify_lib_of_key_change(object_change_type_t change_type,
> + unsigned int parent_object_handle,
> + unsigned int object_handle,
> + void *object_name_pt, int object_name_len,
> + void *key_name_pt, int key_name_len,
> + void *key_value_pt, int key_value_len,
> + void *priv_data_pt);
> +
> +static void confdb_notify_lib_of_new_object(unsigned int parent_object_handle,
> + unsigned int object_handle,
> + uint8_t *name_pt, int name_len,
> + void *priv_data_pt);
> +static void confdb_notify_lib_of_destroyed_object(unsigned int parent_object_handle,
> + uint8_t *name_pt, int name_len,
> + void *priv_data_pt);
> /*
> * Library Handler Definition
> */
> @@ -149,7 +163,7 @@ static struct corosync_lib_handler confdb_lib_engine[] =
> { /* 11 */
> .lib_handler_fn = message_handler_req_lib_confdb_track_stop,
> .response_size = sizeof (mar_res_header_t),
> - .response_id = MESSAGE_RES_CONFDB_TRACK_START,
> + .response_id = MESSAGE_RES_CONFDB_TRACK_STOP,
> .flow_control = COROSYNC_LIB_FLOW_CONTROL_NOT_REQUIRED
> },
> { /* 12 */
> @@ -230,6 +244,11 @@ static int confdb_lib_exit_fn (void *conn)
> {
>
> log_printf(LOG_LEVEL_DEBUG, "exit_fn for conn=%p\n", conn);
> + /* cleanup the object trackers for this client. */
> + api->object_track_stop(confdb_notify_lib_of_key_change,
> + confdb_notify_lib_of_new_object,
> + confdb_notify_lib_of_destroyed_object,
> + api->ipc_conn_partner_get (conn));
> return (0);
> }
>
> @@ -469,14 +488,84 @@ static void message_handler_req_lib_confdb_write (void *conn, void *message)
> api->ipc_conn_send_response(conn, &res_lib_confdb_write, sizeof(res_lib_confdb_write));
> }
>
> -/* TODO: when we have notification in the objdb. */
> +static void confdb_notify_lib_of_key_change(object_change_type_t change_type,
> + unsigned int parent_object_handle,
> + unsigned int object_handle,
> + void *object_name_pt, int object_name_len,
> + void *key_name_pt, int key_name_len,
> + void *key_value_pt, int key_value_len,
> + void *priv_data_pt)
> +{
> + struct res_lib_confdb_key_change_callback res;
> +
> + res.header.size = sizeof(res);
> + res.header.id = MESSAGE_RES_CONFDB_KEY_CHANGE_CALLBACK;
> + res.header.error = SA_AIS_OK;
> +// handle & type
> + res.change_type = change_type;
> + res.parent_object_handle = parent_object_handle;
> + res.object_handle = object_handle;
> +//object
> + memcpy(res.object_name.value, object_name_pt, object_name_len);
> + res.object_name.length = object_name_len;
> +//key name
> + memcpy(res.key_name.value, key_name_pt, key_name_len);
> + res.key_name.length = key_name_len;
> +//key value
> + memcpy(res.key_value.value, key_value_pt, key_value_len);
> + res.key_value.length = key_value_len;
> +
> + api->ipc_conn_send_response(priv_data_pt, &res, sizeof(res));
> +}
> +
> +static void confdb_notify_lib_of_new_object(unsigned int parent_object_handle,
> + unsigned int object_handle,
> + uint8_t *name_pt, int name_len,
> + void *priv_data_pt)
> +{
> + struct res_lib_confdb_object_create_callback res;
> +
> + res.header.size = sizeof(res);
> + res.header.id = MESSAGE_RES_CONFDB_OBJECT_CREATE_CALLBACK;
> + res.header.error = SA_AIS_OK;
> + res.parent_object_handle = parent_object_handle;
> + res.object_handle = object_handle;
> + memcpy(res.name.value, name_pt, name_len);
> + res.name.length = name_len;
> +
> + api->ipc_conn_send_response(priv_data_pt, &res, sizeof(res));
> +}
> +
> +static void confdb_notify_lib_of_destroyed_object(unsigned int parent_object_handle,
> + uint8_t *name_pt, int name_len,
> + void *priv_data_pt)
> +{
> + struct res_lib_confdb_object_destroy_callback res;
> +
> + res.header.size = sizeof(res);
> + res.header.id = MESSAGE_RES_CONFDB_OBJECT_DESTROY_CALLBACK;
> + res.header.error = SA_AIS_OK;
> + res.parent_object_handle = parent_object_handle;
> + memcpy(res.name.value, name_pt, name_len);
> + res.name.length = name_len;
> +
> + api->ipc_conn_send_response(priv_data_pt, &res, sizeof(res));
> +}
> +
> +
> static void message_handler_req_lib_confdb_track_start (void *conn, void *message)
> {
> + struct req_lib_confdb_object_track_start *req = (struct req_lib_confdb_object_track_start *)message;
> mar_res_header_t res;
>
> + api->object_track_start(req->object_handle, req->flags,
> + confdb_notify_lib_of_key_change,
> + confdb_notify_lib_of_new_object,
> + confdb_notify_lib_of_destroyed_object,
> + api->ipc_conn_partner_get (conn));
> res.size = sizeof(res);
> res.id = MESSAGE_RES_CONFDB_TRACK_START;
> - res.error = SA_AIS_ERR_NOT_SUPPORTED;
> + res.error = SA_AIS_OK;
> api->ipc_conn_send_response(conn, &res, sizeof(res));
> }
>
> @@ -484,9 +573,14 @@ static void message_handler_req_lib_confdb_track_stop (void *conn, void *message
> {
> mar_res_header_t res;
>
> + api->object_track_stop(confdb_notify_lib_of_key_change,
> + confdb_notify_lib_of_new_object,
> + confdb_notify_lib_of_destroyed_object,
> + api->ipc_conn_partner_get (conn));
> +
> res.size = sizeof(res);
> res.id = MESSAGE_RES_CONFDB_TRACK_STOP;
> - res.error = SA_AIS_ERR_NOT_SUPPORTED;
> + res.error = SA_AIS_OK;
> api->ipc_conn_send_response(conn, &res, sizeof(res));
> }
>
> diff --git a/corosync/test/testconfdb.c b/corosync/test/testconfdb.c
> index 8ecbf46..90ab3c5 100644
> --- a/corosync/test/testconfdb.c
> +++ b/corosync/test/testconfdb.c
> @@ -47,7 +47,9 @@
>
> /* Callbacks are not supported yet */
> confdb_callbacks_t callbacks = {
> - .confdb_change_notify_fn = NULL,
> + .confdb_key_change_notify_fn = NULL,
> + .confdb_object_create_change_notify_fn = NULL,
> + .confdb_object_delete_change_notify_fn = NULL
> };
>
> /* Recursively dump the object tree */
> diff --git a/corosync/tools/corosync-objctl.c b/corosync/tools/corosync-objctl.c
> index 84f356c..d59c2f6 100644
> --- a/corosync/tools/corosync-objctl.c
> +++ b/corosync/tools/corosync-objctl.c
> @@ -54,6 +54,7 @@ typedef enum {
> ACTION_DELETE,
> ACTION_PRINT_ALL,
> ACTION_PRINT_DEFAULT,
> + ACTION_TRACK,
> } action_types_t;
>
> typedef enum {
> @@ -62,8 +63,32 @@ typedef enum {
> FIND_KEY_ONLY
> } find_object_of_type_t;
>
> +static void tail_key_changed(confdb_handle_t handle,
> + confdb_change_type_t change_type,
> + unsigned int parent_object_handle,
> + unsigned int object_handle,
> + void *object_name,
> + int object_name_len,
> + void *key_name,
> + int key_name_len,
> + void *key_value,
> + int key_value_len);
> +
> +static void tail_object_created(confdb_handle_t handle,
> + unsigned int parent_object_handle,
> + unsigned int object_handle,
> + uint8_t *name_pt,
> + int name_len);
> +
> +static void tail_object_deleted(confdb_handle_t handle,
> + unsigned int parent_object_handle,
> + uint8_t *name_pt,
> + int name_len);
> +
> confdb_callbacks_t callbacks = {
> - .confdb_change_notify_fn = NULL,
> + .confdb_key_change_notify_fn = tail_key_changed,
> + .confdb_object_create_change_notify_fn = tail_object_created,
> + .confdb_object_delete_change_notify_fn = tail_object_deleted,
> };
>
> static int action;
> @@ -377,6 +402,113 @@ static void create_object(confdb_handle_t handle, char * name_pt)
> }
> }
>
> +static void tail_key_changed(confdb_handle_t handle,
> + confdb_change_type_t change_type,
> + unsigned int parent_object_handle,
> + unsigned int object_handle,
> + void *object_name_pt,
> + int object_name_len,
> + void *key_name_pt,
> + int key_name_len,
> + void *key_value_pt,
> + int key_value_len)
> +{
> + char * on = (char*)object_name_pt;
> + char * kn = (char*)key_name_pt;
> + char * kv = (char*)key_value_pt;
> +
> + on[object_name_len] = '\0';
> + kv[key_value_len] = '\0';
> + kn[key_name_len] = '\0';
> + printf("key_changed> %s.%s=%s\n", on, kn, kv);
> +}
> +
> +static void tail_object_created(confdb_handle_t handle,
> + unsigned int parent_object_handle,
> + unsigned int object_handle,
> + uint8_t *name_pt,
> + int name_len)
> +{
> + name_pt[name_len] = '\0';
> + printf("object_created> %s\n", name_pt);
> +}
> +
> +static void tail_object_deleted(confdb_handle_t handle,
> + unsigned int parent_object_handle,
> + uint8_t *name_pt,
> + int name_len)
> +{
> + name_pt[name_len] = '\0';
> +
> + printf("object_deleted> %s\n", name_pt);
> +}
> +
> +static void listen_for_object_changes(confdb_handle_t handle)
> +{
> + int result;
> + fd_set read_fds;
> + int select_fd;
> + SaBoolT quit = SA_FALSE;
> +
> + FD_ZERO (&read_fds);
> + confdb_fd_get(handle, &select_fd);
> + printf ("Type \"q\" to finish\n");
> + do {
> + FD_SET (select_fd, &read_fds);
> + FD_SET (STDIN_FILENO, &read_fds);
> + result = select (select_fd + 1, &read_fds, 0, 0, 0);
> + if (result == -1) {
> + perror ("select\n");
> + }
> + if (FD_ISSET (STDIN_FILENO, &read_fds)) {
> + char inbuf[3];
> +
> + fgets(inbuf, sizeof(inbuf), stdin);
> + if (strncmp(inbuf, "q", 1) == 0)
> + quit = SA_TRUE;
> + }
> + if (FD_ISSET (select_fd, &read_fds)) {
> + if (confdb_dispatch (handle, CONFDB_DISPATCH_ALL) != CONFDB_OK)
> + exit(1);
> + }
> + } while (result && quit == SA_FALSE);
> +
> + confdb_stop_track_changes(handle);
> +
> +}
> +
> +static void track_object(confdb_handle_t handle, char * name_pt)
> +{
> + confdb_error_t res;
> + uint32_t obj_handle;
> +
> + res = find_object (handle, name_pt, FIND_OBJECT_ONLY, &obj_handle);
> +
> + if (res != CONFDB_OK) {
> + fprintf (stderr, "Could not find object \"%s\". Error %d\n",
> + name_pt, res);
> + return;
> + }
> +
> + res = confdb_track_changes (handle, obj_handle, CONFDB_TRACK_DEPTH_RECURSIVE);
> + if (res != CONFDB_OK) {
> + fprintf (stderr, "Could not enable tracking on object \"%s\". Error %d\n",
> + name_pt, res);
> + return;
> + }
> +}
> +
> +static void stop_tracking(confdb_handle_t handle)
> +{
> + confdb_error_t res;
> +
> + res = confdb_stop_track_changes (handle);
> + if (res != CONFDB_OK) {
> + fprintf (stderr, "Could not stop tracking. Error %d\n", res);
> + return;
> + }
> +}
> +
> static void delete_object(confdb_handle_t handle, char * name_pt)
> {
> confdb_error_t res;
> @@ -425,7 +557,7 @@ int main (int argc, char *argv[]) {
> action = ACTION_READ;
>
> for (;;){
> - c = getopt (argc,argv,"hawcdp:");
> + c = getopt (argc,argv,"hawcdtp:");
> if (c==-1) {
> break;
> }
> @@ -450,6 +582,9 @@ int main (int argc, char *argv[]) {
> case 'w':
> action = ACTION_WRITE;
> break;
> + case 't':
> + action = ACTION_TRACK;
> + break;
> default :
> action = ACTION_READ;
> break;
> @@ -485,9 +620,17 @@ int main (int argc, char *argv[]) {
> case ACTION_DELETE:
> delete_object(handle, argv[optind++]);
> break;
> + case ACTION_TRACK:
> + track_object(handle, argv[optind++]);
> + break;
> }
> }
>
> + if (action == ACTION_TRACK) {
> + listen_for_object_changes(handle);
> + stop_tracking(handle);
> + }
> +
> result = confdb_finalize (handle);
> if (result != CONFDB_OK) {
> fprintf (stderr, "Error finalizing objdb API. Error %d\n", result);
More information about the Openais
mailing list