/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#ifndef	_CONFIGD_H
#define	_CONFIGD_H

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <door.h>
#include <pthread.h>
#include <string.h>
#include <sys/types.h>

#include <libscf.h>
#include <repcache_protocol.h>
#include <libuutil.h>

#include <configd_exit.h>

#ifdef	__cplusplus
extern "C" {
#endif

/*
 * Lock order:
 *
 *	client lock
 *		iter locks, in ID order
 *		entity locks, in ID order
 *
 *		(any iter/entity locks)
 *			backend locks (NORMAL, then NONPERSIST)
 *				rc_node lock
 *					children's rc_node lock
 *				cache bucket lock
 *					rc_node lock[*]
 *
 *	* only one node may be grabbed while holding a bucket lock
 *
 *	leaf locks:  (no other locks may be aquired while holding one)
 *		rc_pg_notify_lock
 */

/*
 * Returns the minimum size for a structure of type 't' such
 * that it is safe to access field 'f'.
 */
#define	offsetofend(t, f)	(offsetof(t, f) + sizeof (((t *)0)->f))

/*
 * We want MUTEX_HELD, but we also want pthreads.  So we're stuck with this.
 */
struct _lwp_mutex_t;
extern int _mutex_held(struct _lwp_mutex_t *);
#define	MUTEX_HELD(m)		_mutex_held((struct _lwp_mutex_t *)(m))

/*
 * Maximum levels of composition.
 */
#define	COMPOSITION_DEPTH	2

/*
 * The default locations of the repository dbs
 */
#define	REPOSITORY_DB	"/etc/svc/repository.db"
#define	NONPERSIST_DB	"/etc/svc/volatile/svc_nonpersist.db"

#define	CONFIGD_CORE	"core.%f.%t.%p"

#ifndef NDEBUG
#define	bad_error(f, e)							\
	uu_warn("%s:%d: %s() returned bad error %d.  Aborting.\n",	\
	    __FILE__, __LINE__, f, e);					\
	abort()
#else
#define	bad_error(f, e)		abort()
#endif

typedef enum backend_type {
	BACKEND_TYPE_NORMAL		= 0,
	BACKEND_TYPE_NONPERSIST,
	BACKEND_TYPE_TOTAL			/* backend use only */
} backend_type_t;

/*
 * pre-declare rc_* types
 */
typedef struct rc_node rc_node_t;
typedef struct rc_snapshot rc_snapshot_t;
typedef struct rc_snaplevel rc_snaplevel_t;

/*
 * notification layer -- protected by rc_pg_notify_lock
 */
typedef struct rc_notify_info rc_notify_info_t;
typedef struct rc_notify_delete rc_notify_delete_t;

#define	RC_NOTIFY_MAX_NAMES	4	/* enough for now */

typedef struct rc_notify {
	uu_list_node_t	rcn_list_node;
	rc_node_t	*rcn_node;
	rc_notify_info_t *rcn_info;
	rc_notify_delete_t *rcn_delete;
} rc_notify_t;

struct rc_notify_delete {
	rc_notify_t rnd_notify;
	char rnd_fmri[REP_PROTOCOL_FMRI_LEN];
};

struct rc_notify_info {
	uu_list_node_t	rni_list_node;
	rc_notify_t	rni_notify;
	const char	*rni_namelist[RC_NOTIFY_MAX_NAMES];
	const char	*rni_typelist[RC_NOTIFY_MAX_NAMES];

	int		rni_flags;
	int		rni_waiters;
	pthread_cond_t	rni_cv;
};
#define	RC_NOTIFY_ACTIVE	0x00000001
#define	RC_NOTIFY_DRAIN		0x00000002
#define	RC_NOTIFY_EMPTYING	0x00000004

typedef struct rc_node_pg_notify {
	uu_list_node_t	rnpn_node;
	int		rnpn_fd;
	rc_node_t	*rnpn_pg;
} rc_node_pg_notify_t;

/*
 * cache layer
 */

/*
 * The 'key' for the main object hash.  main_id is the main object
 * identifier.  The rl_ids array contains:
 *
 *	TYPE		RL_IDS
 *	scope		unused
 *	service		unused
 *	instance	{service_id}
 *	snapshot	{service_id, instance_id}
 *	snaplevel	{service_id, instance_id, name_id, snapshot_id}
 *	propertygroup	{service_id, (instance_id or 0), (name_id or 0),
 *			    (snapshot_id or 0), (l_id or 0)}
 *	property	{service_id, (instance_id or 0), (name_id or 0),
 *			    (snapshot_id or 0), (l_id or 0), pg_id, gen_id}
 */
#define	ID_SERVICE	0
#define	ID_INSTANCE	1
#define	ID_NAME		2
#define	ID_SNAPSHOT	3
#define	ID_LEVEL	4
#define	ID_PG		5
#define	ID_GEN		6
#define	MAX_IDS	7
typedef struct rc_node_lookup {
	uint16_t	rl_type;		/* REP_PROTOCOL_ENTITY_* */
	uint16_t	rl_backend;		/* BACKEND_TYPE_* */
	uint32_t	rl_main_id;		/* primary identifier */
	uint32_t	rl_ids[MAX_IDS];	/* context */
} rc_node_lookup_t;

struct rc_node {
	/*
	 * read-only data
	 */
	rc_node_lookup_t rn_id;			/* must be first */
	uint32_t	rn_hash;
	const char	*rn_name;

	/*
	 * type-specific state
	 * (if space becomes an issue, these can become a union)
	 */

	/*
	 * Used by instances, snapshots, and "composed property groups" only.
	 * These are the entities whose properties should appear composed when
	 * this entity is traversed by a composed iterator.  0 is the top-most
	 * entity, down to COMPOSITION_DEPTH - 1.
	 */
	rc_node_t	*rn_cchain[COMPOSITION_DEPTH];

	/*
	 * used by property groups only
	 */
	const char	*rn_type;
	uint32_t	rn_pgflags;
	uint32_t	rn_gen_id;
	uu_list_t	*rn_pg_notify_list;	/* prot by rc_pg_notify_lock */
	rc_notify_t	rn_notify;		/* prot by rc_pg_notify_lock */

	/*
	 * used by properties only
	 */
	rep_protocol_value_type_t rn_valtype;
	const char	*rn_values;		/* protected by rn_lock */
	size_t		rn_values_count;	/* protected by rn_lock */
	size_t		rn_values_size;		/* protected by rn_lock */

	/*
	 * used by snapshots only
	 */
	uint32_t	rn_snapshot_id;
	rc_snapshot_t	*rn_snapshot;		/* protected by rn_lock */

	/*
	 * used by snaplevels only
	 */
	rc_snaplevel_t	*rn_snaplevel;

	/*
	 * mutable state
	 */
	pthread_mutex_t	rn_lock;
	pthread_cond_t	rn_cv;
	uint32_t	rn_flags;
	uint32_t	rn_refs;		/* reference count */
	uint32_t	rn_other_refs;		/* atomic refcount */
	uint32_t	rn_other_refs_held;	/* for 1->0 transitions */

	uu_list_t	*rn_children;
	uu_list_node_t	rn_sibling_node;

	rc_node_t	*rn_parent;		/* set if on child list */
	rc_node_t	*rn_former;		/* next former node */
	rc_node_t	*rn_parent_ref;		/* reference count target */

	/*
	 * external state (protected by hash chain lock)
	 */
	rc_node_t	*rn_hash_next;
};

/*
 * flag ordering:
 *	RC_DYING
 *		RC_NODE_CHILDREN_CHANGING
 *		RC_NODE_CREATING_CHILD
 *		RC_NODE_USING_PARENT
 *			RC_NODE_IN_TX
 *
 * RC_NODE_USING_PARENT is special, because it lets you proceed up the tree,
 * in the reverse of the usual locking order.  Because of this, there are
 * limitations on what you can do while holding it.  While holding
 * RC_NODE_USING_PARENT, you may:
 *	bump or release your parent's reference count
 *	access fields in your parent
 *	hold RC_NODE_USING_PARENT in the parent, proceeding recursively.
 *
 * If you are only holding *one* node's RC_NODE_USING_PARENT, and:
 *	you are *not* proceeding recursively, you can hold your
 *	    immediate parent's RC_NODE_CHILDREN_CHANGING flag.
 *	you hold your parent's RC_NODE_CHILDREN_CHANGING flag, you can add
 *	    RC_NODE_IN_TX to your flags.
 *	you want to grab a flag in your parent, you must lock your parent,
 *	    lock yourself, drop RC_NODE_USING_PARENT, unlock yourself,
 *	    then proceed to manipulate the parent.
 */
#define	RC_NODE_CHILDREN_CHANGING	0x00000001 /* child list in flux */
#define	RC_NODE_HAS_CHILDREN		0x00000002 /* child list is accurate */

#define	RC_NODE_IN_PARENT		0x00000004 /* I'm in my parent's list */
#define	RC_NODE_USING_PARENT		0x00000008 /* parent ptr in use */
#define	RC_NODE_CREATING_CHILD		0x00000010 /* a create is in progress */
#define	RC_NODE_IN_TX			0x00000020 /* a tx is in progess */

#define	RC_NODE_OLD			0x00000400 /* out-of-date object */
#define	RC_NODE_ON_FORMER		0x00000800 /* on an rn_former list */

#define	RC_NODE_PARENT_REF		0x00001000 /* parent_ref in use */
#define	RC_NODE_UNREFED			0x00002000 /* unref processing active */
#define	RC_NODE_DYING			0x00004000 /* node is being deleted */
#define	RC_NODE_DEAD			0x00008000 /* node has been deleted */

#define	RC_NODE_DYING_FLAGS						\
	(RC_NODE_CHILDREN_CHANGING | RC_NODE_IN_TX | RC_NODE_DYING |	\
	    RC_NODE_CREATING_CHILD)

#define	RC_NODE_WAITING_FLAGS						\
	(RC_NODE_DYING_FLAGS | RC_NODE_USING_PARENT)


typedef struct rc_node_ptr {
	rc_node_t	*rnp_node;
	char		rnp_authorized;		/* transaction pre-authed */
	char		rnp_deleted;		/* object was deleted */
} rc_node_ptr_t;

#define	NODE_PTR_NOT_HELD(npp) \
	    ((npp)->rnp_node == NULL || !MUTEX_HELD(&(npp)->rnp_node->rn_lock))

typedef int rc_iter_filter_func(rc_node_t *, void *);

typedef struct rc_node_iter {
	rc_node_t	*rni_parent;
	int		rni_clevel;	/* index into rni_parent->rn_cchain[] */
	rc_node_t	*rni_iter_node;
	uu_list_walk_t	*rni_iter;
	uint32_t	rni_type;

	/*
	 * for normal walks
	 */
	rc_iter_filter_func *rni_filter;
	void		*rni_filter_arg;

	/*
	 * for value walks
	 */
	uint32_t	rni_offset;		/* next value offset */
	uint32_t	rni_last_offset;	/* previous value offset */
} rc_node_iter_t;

typedef struct rc_node_tx {
	rc_node_ptr_t	rnt_ptr;
	int		rnt_authorized;		/* No need to check anymore. */
} rc_node_tx_t;


typedef struct cache_bucket {
	pthread_mutex_t	cb_lock;
	rc_node_t	*cb_head;

	char		cb_pad[64 - sizeof (pthread_mutex_t) -
			    2 * sizeof (rc_node_t *)];
} cache_bucket_t;

/*
 * Snapshots
 */
struct rc_snapshot {
	uint32_t	rs_snap_id;

	pthread_mutex_t	rs_lock;
	pthread_cond_t	rs_cv;

	uint32_t	rs_flags;
	uint32_t	rs_refcnt;	/* references from rc_nodes */
	uint32_t	rs_childref;	/* references to children */

	rc_snaplevel_t	*rs_levels;	/* list of levels */
	rc_snapshot_t	*rs_hash_next;
};
#define	RC_SNAPSHOT_FILLING	0x00000001	/* rs_levels changing */
#define	RC_SNAPSHOT_READY	0x00000002
#define	RC_SNAPSHOT_DEAD	0x00000004	/* no resources */

typedef struct rc_snaplevel_pgs {
	uint32_t	rsp_pg_id;
	uint32_t	rsp_gen_id;
} rc_snaplevel_pgs_t;

struct rc_snaplevel {
	rc_snapshot_t	*rsl_parent;
	uint32_t	rsl_level_num;
	uint32_t	rsl_level_id;

	uint32_t	rsl_service_id;
	uint32_t	rsl_instance_id;

	const char	*rsl_scope;
	const char	*rsl_service;
	const char	*rsl_instance;

	rc_snaplevel_t	*rsl_next;
};

/*
 * Client layer -- the IDs fields must be first, in order for the search
 * routines to work correctly.
 */
enum repcache_txstate {
	REPCACHE_TX_INIT,
	REPCACHE_TX_SETUP,
	REPCACHE_TX_COMMITTED
};

typedef struct repcache_entity {
	uint32_t	re_id;
	uu_avl_node_t	re_link;
	uint32_t	re_changeid;

	pthread_mutex_t	re_lock;
	uint32_t	re_type;
	rc_node_ptr_t	re_node;
	enum repcache_txstate re_txstate;	/* property groups only */
} repcache_entity_t;

typedef struct repcache_iter {
	uint32_t	ri_id;
	uu_avl_node_t	ri_link;

	uint32_t	ri_type;	/* result type */

	pthread_mutex_t	ri_lock;
	uint32_t	ri_sequence;
	rc_node_iter_t	*ri_iter;
} repcache_iter_t;

typedef struct repcache_client {
	/*
	 * constants
	 */
	uint32_t	rc_id;		/* must be first */
	int		rc_all_auths;	/* bypass auth checks */
	uint32_t	rc_debug;	/* debug flags */
	pid_t		rc_pid;		/* pid of opening process */
	door_id_t	rc_doorid;	/* a globally unique identifier */
	int		rc_doorfd;	/* our door's FD */

	/*
	 * client list linkage, protected by hash chain lock
	 */
	uu_list_node_t	rc_link;

	/*
	 * notification information, protected by rc_node layer
	 */
	rc_node_pg_notify_t	rc_pg_notify;
	rc_notify_info_t	rc_notify_info;

	/*
	 * client_wait output, only usable by rc_notify_thr
	 */
	rc_node_ptr_t	rc_notify_ptr;

	/*
	 * register sets, protected by rc_lock
	 */
	uu_avl_t	*rc_entities;
	uu_avl_t	*rc_iters;

	/*
	 * Variables, protected by rc_lock
	 */
	int		rc_refcnt;	/* in-progress door calls */
	int		rc_flags;	/* state */
	uint32_t	rc_changeid;	/* used to make backups idempotent */
	pthread_t	rc_insert_thr;	/* single thread trying to insert */
	pthread_t	rc_notify_thr;	/* single thread waiting for notify */
	pthread_cond_t	rc_cv;
	pthread_mutex_t	rc_lock;
} repcache_client_t;
#define	RC_CLIENT_DEAD			0x00000001

typedef struct client_bucket {
	pthread_mutex_t	cb_lock;
	uu_list_t	*cb_list;
	char ch_pad[64 - sizeof (pthread_mutex_t) - sizeof (uu_list_t *)];
} client_bucket_t;

enum rc_ptr_type {
	RC_PTR_TYPE_ENTITY = 1,
	RC_PTR_TYPE_ITER
};

typedef struct request_log_ptr {
	enum rc_ptr_type	rlp_type;
	uint32_t		rlp_id;
	void			*rlp_ptr; /* repcache_{entity,iter}_t */
	void			*rlp_data;	/* rc_node, for ENTITY only */
} request_log_ptr_t;

#define	MAX_PTRS	3

/*
 * rl_start through rl_client cannot move without changing start_log()
 */
typedef struct request_log_entry {
	hrtime_t		rl_start;
	hrtime_t		rl_end;
	pthread_t		rl_tid;
	uint32_t		rl_clientid;
	repcache_client_t	*rl_client;
	enum rep_protocol_requestid rl_request;
	rep_protocol_responseid_t rl_response;
	int			rl_num_ptrs;
	request_log_ptr_t	rl_ptrs[MAX_PTRS];
} request_log_entry_t;

/*
 * thread information
 */
typedef enum thread_state {
	TI_CREATED,
	TI_DOOR_RETURN,
	TI_SIGNAL_WAIT,
	TI_MAIN_DOOR_CALL,
	TI_CLIENT_CALL
} thread_state_t;

typedef struct thread_info {
	pthread_t	ti_thread;
	uu_list_node_t	ti_node;		/* for list of all thread */

	/*
	 * per-thread globals
	 */
	ucred_t		*ti_ucred;		/* for credential lookups */
	int		ti_ucred_read;		/* ucred holds current creds */

	/*
	 * per-thread state information, for debuggers
	 */
	hrtime_t	ti_lastchange;

	thread_state_t	ti_state;
	thread_state_t	ti_prev_state;

	repcache_client_t *ti_active_client;
	request_log_entry_t	ti_log;

	struct rep_protocol_request *ti_client_request;
	repository_door_request_t *ti_main_door_request;

} thread_info_t;

/*
 * Backend layer
 */
typedef struct backend_query backend_query_t;
typedef struct backend_tx backend_tx_t;

/*
 * configd.c
 */
int create_connection(ucred_t *cred, repository_door_request_t *rp,
    size_t rp_size, int *out_fd);

thread_info_t *thread_self(void);
void thread_newstate(thread_info_t *, thread_state_t);
ucred_t *get_ucred(void);
int ucred_is_privileged(ucred_t *);

void configd_critical(const char *, ...);
void configd_vcritical(const char *, va_list);

extern int is_main_repository;
extern int max_repository_backups;

/*
 * maindoor.c
 */
int setup_main_door(const char *);

/*
 * client.c
 */
int create_client(pid_t, uint32_t, int, int *);
int client_init(void);
int client_is_privileged(void);
void log_enter(request_log_entry_t *);

/*
 * rc_node.c, backend/cache interfaces (rc_node_t)
 */
int rc_node_init();
int rc_check_type_name(uint32_t, const char *);

void rc_node_rele(rc_node_t *);
rc_node_t *rc_node_setup(rc_node_t *, rc_node_lookup_t *,
    const char *, rc_node_t *);
rc_node_t *rc_node_setup_pg(rc_node_t *, rc_node_lookup_t *, const char *,
    const char *, uint32_t, uint32_t, rc_node_t *);
rc_node_t *rc_node_setup_snapshot(rc_node_t *, rc_node_lookup_t *, const char *,
    uint32_t, rc_node_t *);
rc_node_t *rc_node_setup_snaplevel(rc_node_t *, rc_node_lookup_t *,
    rc_snaplevel_t *, rc_node_t *);
int rc_node_create_property(rc_node_t *, rc_node_lookup_t *,
    const char *, rep_protocol_value_type_t, const char *, size_t, size_t);

rc_node_t *rc_node_alloc(void);
void rc_node_destroy(rc_node_t *);

/*
 * rc_node.c, client interface (rc_node_ptr_t, rc_node_iter_t)
 */
void rc_node_ptr_init(rc_node_ptr_t *);
int rc_local_scope(uint32_t, rc_node_ptr_t *);

void rc_node_clear(rc_node_ptr_t *, int);
void rc_node_ptr_assign(rc_node_ptr_t *, const rc_node_ptr_t *);
int rc_node_name(rc_node_ptr_t *, char *, size_t, uint32_t, size_t *);
int rc_node_fmri(rc_node_ptr_t *, char *, size_t, size_t *);
int rc_node_parent_type(rc_node_ptr_t *, uint32_t *);
int rc_node_get_child(rc_node_ptr_t *, const char *, uint32_t, rc_node_ptr_t *);
int rc_node_get_parent(rc_node_ptr_t *, uint32_t, rc_node_ptr_t *);
int rc_node_get_property_type(rc_node_ptr_t *, rep_protocol_value_type_t *);
int rc_node_get_property_value(rc_node_ptr_t *,
    struct rep_protocol_value_response *, size_t *);
int rc_node_create_child(rc_node_ptr_t *, uint32_t, const char *,
    rc_node_ptr_t *);
int rc_node_create_child_pg(rc_node_ptr_t *, uint32_t, const char *,
    const char *, uint32_t, rc_node_ptr_t *);
int rc_node_update(rc_node_ptr_t *);
int rc_node_delete(rc_node_ptr_t *);
int rc_node_next_snaplevel(rc_node_ptr_t *, rc_node_ptr_t *);

int rc_node_setup_iter(rc_node_ptr_t *, rc_node_iter_t **, uint32_t,
    size_t, const char *);

int rc_iter_next(rc_node_iter_t *, rc_node_ptr_t *, uint32_t);
int rc_iter_next_value(rc_node_iter_t *, struct rep_protocol_value_response *,
    size_t *, int);
void rc_iter_destroy(rc_node_iter_t **);

int rc_node_setup_tx(rc_node_ptr_t *, rc_node_ptr_t *);
int rc_tx_commit(rc_node_ptr_t *, const void *, size_t);

void rc_pg_notify_init(rc_node_pg_notify_t *);
int rc_pg_notify_setup(rc_node_pg_notify_t *, rc_node_ptr_t *, int);
void rc_pg_notify_fini(rc_node_pg_notify_t *);

void rc_notify_info_init(rc_notify_info_t *);
int rc_notify_info_add_name(rc_notify_info_t *, const char *);
int rc_notify_info_add_type(rc_notify_info_t *, const char *);
int rc_notify_info_wait(rc_notify_info_t *, rc_node_ptr_t *, char *, size_t);
void rc_notify_info_fini(rc_notify_info_t *);

int rc_snapshot_take_new(rc_node_ptr_t *, const char *,
    const char *, const char *, rc_node_ptr_t *);
int rc_snapshot_take_attach(rc_node_ptr_t *, rc_node_ptr_t *);
int rc_snapshot_attach(rc_node_ptr_t *, rc_node_ptr_t *);

/*
 * file_object.c
 */
int object_fill_children(rc_node_t *);
int object_create(rc_node_t *, uint32_t, const char *, rc_node_t **);
int object_create_pg(rc_node_t *, uint32_t, const char *, const char *,
    uint32_t, rc_node_t **);

int object_delete(rc_node_t *);
void object_free_values(const char *, uint32_t, size_t, size_t);

int object_fill_snapshot(rc_snapshot_t *);

int object_snapshot_take_new(rc_node_t *, const char *, const char *,
    const char *, rc_node_t **);
int object_snapshot_attach(rc_node_lookup_t *, uint32_t *, int);

/*
 * object.c
 */
int object_tx_commit(rc_node_lookup_t *, const void *, size_t, uint32_t *);

/*
 * snapshot.c
 */
int rc_snapshot_get(uint32_t, rc_snapshot_t **);
void rc_snapshot_rele(rc_snapshot_t *);
void rc_snaplevel_hold(rc_snaplevel_t *);
void rc_snaplevel_rele(rc_snaplevel_t *);

/*
 * backend.c
 */
int backend_init(const char *, const char *, int);
void backend_fini(void);

rep_protocol_responseid_t backend_create_backup(const char *);

/*
 * call on any database inconsistency -- cleans up state as best it can,
 * and exits with a "Database Bad" error code.
 */
void backend_panic(const char *, ...) __NORETURN;
#pragma rarely_called(backend_panic)

backend_query_t *backend_query_alloc(void);
void backend_query_append(backend_query_t *, const char *);
void backend_query_add(backend_query_t *, const char *, ...);
void backend_query_free(backend_query_t *);

typedef int backend_run_callback_f(void *data, int columns, char **vals,
    char **names);
#define	BACKEND_CALLBACK_CONTINUE	0
#define	BACKEND_CALLBACK_ABORT		1

backend_run_callback_f backend_fail_if_seen;	/* aborts TX if called */

int backend_run(backend_type_t, backend_query_t *,
    backend_run_callback_f *, void *);

int backend_tx_begin(backend_type_t, backend_tx_t **);
int backend_tx_begin_ro(backend_type_t, backend_tx_t **);
void backend_tx_end_ro(backend_tx_t *);

enum id_space {
	BACKEND_ID_SERVICE_INSTANCE,
	BACKEND_ID_PROPERTYGRP,
	BACKEND_ID_GENERATION,
	BACKEND_ID_PROPERTY,
	BACKEND_ID_VALUE,
	BACKEND_ID_SNAPNAME,
	BACKEND_ID_SNAPSHOT,
	BACKEND_ID_SNAPLEVEL,
	BACKEND_ID_INVALID	/* always illegal */
};

uint32_t backend_new_id(backend_tx_t *, enum id_space);
int backend_tx_run_update(backend_tx_t *, const char *, ...);
int backend_tx_run_update_changed(backend_tx_t *, const char *, ...);
int backend_tx_run_single_int(backend_tx_t *tx, backend_query_t *q,
    uint32_t *buf);
int backend_tx_run(backend_tx_t *, backend_query_t *,
    backend_run_callback_f *, void *);

int backend_tx_commit(backend_tx_t *);
void backend_tx_rollback(backend_tx_t *);

#ifdef	__cplusplus
}
#endif

#endif	/* _CONFIGD_H */