[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