/* Copyright (C) 2005 Red Hat, Inc. */

#ifndef _SEMANAGE_DATABASE_H_
#define _SEMANAGE_DATABASE_H_

#ifndef DBASE_RECORD_DEFINED
typedef void *record_t;
typedef void *record_key_t;
#define DBASE_RECORD_DEFINED
#endif

#ifndef DBASE_DEFINED
typedef void *dbase_t;
#define DBASE_DEFINED
#endif

/* Circular dependency */
struct semanage_handle;

/* RECORD interface - method table */
typedef struct record_table {

	/* Create a record */
	int (*create) (struct semanage_handle * handle, record_t ** rec);

	/* Extract key from record */
	int (*key_extract) (struct semanage_handle * handle,
			    const record_t * rec, record_key_t ** key);

	/* Free record key */
	void (*key_free) (record_key_t * key);

	/* Return 0 if the record matches the key, 
	 * -1 if the key represents a record that should
	 * be ordered before this record, and 1 if vice-versa */
	int (*compare) (const record_t * rec, const record_key_t * key);

	/* Return 0 if the record matches record2,
	 * -1 if record2 should be ordered before this record,
	 * and 1 if vice-versa */
	int (*compare2) (const record_t * rec, const record_t * rec2);

	/* Same as above, but dereferences the pointer first.
	 * This function is intenteded to be used as a qsort
	 * comparator. */
	int (*compare2_qsort) (const record_t ** rec, const record_t ** rec2);

	/* Deep-copy clone of this record */
	int (*clone) (struct semanage_handle * handle,
		      const record_t * rec, record_t ** new_rec);

	/* Deallocate record resources. Must sucessfully handle NULL. */
	void (*free) (record_t * rec);

} record_table_t;

/* DBASE interface - method table */
typedef struct dbase_table {

	/* --------------- Database Functionality ----------- */

	/* Note: In all the functions below, the key is property
	 * of the caller, and will not be modified by the database. 
	 * In add/set/modify, the data is also property of the caller */

	/* Add the specified record to
	 * the database. No check for duplicates is performed */
	int (*add) (struct semanage_handle * handle,
		    dbase_t * dbase,
		    const record_key_t * key, const record_t * data);

	/* Add the specified record to the  
	 * database if it not present. 
	 * If it's present, replace it
	 */
	int (*modify) (struct semanage_handle * handle,
		       dbase_t * dbase,
		       const record_key_t * key, const record_t * data);

	/* Modify the specified record in the database
	 * if it is present. Fail if it does not yet exist
	 */
	int (*set) (struct semanage_handle * handle,
		    dbase_t * dbase,
		    const record_key_t * key, const record_t * data);

	/* Delete a record */
	int (*del) (struct semanage_handle * handle,
		    dbase_t * dbase, const record_key_t * key);

	/* Clear all records, and leave the database in
	 * cached, modified state. This function does 
	 * not require a call to cache() */
	int (*clear) (struct semanage_handle * handle, dbase_t * dbase);

	/* Retrieve a record 
	 * 
	 * Note: the resultant record
	 * becomes property of the caller, and
	 * must be freed accordingly */

	int (*query) (struct semanage_handle * handle,
		      dbase_t * dbase,
		      const record_key_t * key, record_t ** response);

	/* Check if a record exists */
	int (*exists) (struct semanage_handle * handle,
		       dbase_t * dbase,
		       const record_key_t * key, int *response);

	/* Count the number of records */
	int (*count) (struct semanage_handle * handle,
		      dbase_t * dbase, unsigned int *response);

	/* Execute the specified handler over 
	 * the records of this database. The handler
	 * can signal a successful exit by returning 1,
	 * an error exit by returning -1, and continue by
	 * returning 0
	 * 
	 * Note: The record passed into the iterate handler
	 * may or may not persist after the handler invocation,
	 * and writing to it has unspecified behavior. It *must*
	 * be cloned if modified, or preserved.
	 * 
	 * Note: The iterate handler may not invoke any other
	 * semanage read functions outside a transaction. It is only
	 * reentrant while in transaction. The iterate handler may
	 * not modify the underlying database.
	 */
	int (*iterate) (struct semanage_handle * handle,
			dbase_t * dbase,
			int (*fn) (const record_t * record,
				   void *varg), void *fn_arg);

	/* Construct a list of all records in this database
	 * 
	 * Note: The list returned becomes property of the caller,
	 * and must be freed accordingly. 
	 */
	int (*list) (struct semanage_handle * handle,
		     dbase_t * dbase,
		     record_t *** records, unsigned int *count);

	/* ---------- Cache/Transaction Management ---------- */

	/* Cache the database (if supported).
	 * This function must be invoked before using
	 * any of the database functions above. It may be invoked
	 * multiple times, and will update the cache if a commit
	 * occured between invocations */
	int (*cache) (struct semanage_handle * handle, dbase_t * dbase);

	/* Forgets all changes that haven't been written
	 * to the database backend */
	void (*drop_cache) (dbase_t * dbase);

	/* Checks if there are any changes not written to the backend */
	int (*is_modified) (dbase_t * dbase);

	/* Writes the database changes to its backend */
	int (*flush) (struct semanage_handle * handle, dbase_t * dbase);

	/* ------------- Polymorphism ----------------------- */

	/* Retrieves the record table for this database,
	 * which specifies how to perform basic operations
	 * on each record. */
	record_table_t *(*get_rtable) (dbase_t * dbase);

} dbase_table_t;

typedef struct dbase_config {

	/* Database state */
	dbase_t *dbase;

	/* Database methods */
	dbase_table_t *dtable;

} dbase_config_t;

extern int dbase_add(struct semanage_handle *handle,
		     dbase_config_t * dconfig,
		     const record_key_t * key, const record_t * data);

extern int dbase_modify(struct semanage_handle *handle,
			dbase_config_t * dconfig,
			const record_key_t * key, const record_t * data);

extern int dbase_set(struct semanage_handle *handle,
		     dbase_config_t * dconfig,
		     const record_key_t * key, const record_t * data);

extern int dbase_del(struct semanage_handle *handle,
		     dbase_config_t * dconfig, const record_key_t * key);

extern int dbase_query(struct semanage_handle *handle,
		       dbase_config_t * dconfig,
		       const record_key_t * key, record_t ** response);

extern int dbase_exists(struct semanage_handle *handle,
			dbase_config_t * dconfig,
			const record_key_t * key, int *response);

extern int dbase_count(struct semanage_handle *handle,
		       dbase_config_t * dconfig, unsigned int *response);

extern int dbase_iterate(struct semanage_handle *handle,
			 dbase_config_t * dconfig,
			 int (*fn) (const record_t * record,
				    void *fn_arg), void *fn_arg);

extern int dbase_list(struct semanage_handle *handle,
		      dbase_config_t * dconfig,
		      record_t *** records, unsigned int *count);

#endif