/*-
 * See the file LICENSE for redistribution information.
 *
 * Copyright (c) 1999, 2013 Oracle and/or its affiliates.  All rights reserved.
 *
 * $Id$
 */

#include "db_config.h"

#include "db_int.h"
#ifdef HAVE_SYSTEM_INCLUDE_FILES
#include <tcl.h>
#endif
#include "dbinc/tcl_db.h"

#ifdef CONFIG_TEST
typedef struct {
	const char *name;
	u_int32_t value;
} NAMEMAP;

static const NAMEMAP rep_ack_policies[] = {
	{"all",			DB_REPMGR_ACKS_ALL},
	{"allavailable",	DB_REPMGR_ACKS_ALL_AVAILABLE},
	{"allpeers",		DB_REPMGR_ACKS_ALL_PEERS},
	{"none",		DB_REPMGR_ACKS_NONE},
	{"one",			DB_REPMGR_ACKS_ONE},
	{"onepeer",		DB_REPMGR_ACKS_ONE_PEER},
	{"quorum",		DB_REPMGR_ACKS_QUORUM},
	{NULL,			0}
};

static const NAMEMAP rep_config_types[] = {
	{"autoinit",		DB_REP_CONF_AUTOINIT},
	{"autorollback",	DB_REP_CONF_AUTOROLLBACK},
	{"bulk",		DB_REP_CONF_BULK},
	{"delayclient",		DB_REP_CONF_DELAYCLIENT},
	{"inmem",		DB_REP_CONF_INMEM},
	{"lease",		DB_REP_CONF_LEASE},
	{"mgr2sitestrict",	DB_REPMGR_CONF_2SITE_STRICT},
	{"mgrelections",	DB_REPMGR_CONF_ELECTIONS},
	{"nowait",		DB_REP_CONF_NOWAIT},
	{NULL,			0}
};

static const NAMEMAP rep_timeout_types[] = {
	{"ack",			DB_REP_ACK_TIMEOUT},
	{"checkpoint_delay",	DB_REP_CHECKPOINT_DELAY},
	{"connection_retry",	DB_REP_CONNECTION_RETRY},
	{"election",		DB_REP_ELECTION_TIMEOUT},
	{"election_retry",	DB_REP_ELECTION_RETRY},
	{"full_election",	DB_REP_FULL_ELECTION_TIMEOUT},
	{"heartbeat_monitor",	DB_REP_HEARTBEAT_MONITOR},
	{"heartbeat_send",	DB_REP_HEARTBEAT_SEND},
	{"lease",		DB_REP_LEASE_TIMEOUT},
	{NULL,			0}
};

static int tcl_RepNumberToName __P((const NAMEMAP *, u_int32_t, const char **));

/*
 * tcl_RepConfig --
 *	Call DB_ENV->rep_set_config().
 *
 * PUBLIC: int tcl_RepConfig
 * PUBLIC:     __P((Tcl_Interp *, DB_ENV *, Tcl_Obj *));
 */
int
tcl_RepConfig(interp, dbenv, list)
	Tcl_Interp *interp;		/* Interpreter */
	DB_ENV *dbenv;			/* Environment pointer */
	Tcl_Obj *list;			/* {which on|off} */
{
	static const char *confonoff[] = {
		"off",
		"on",
		NULL
	};
	enum confonoff {
		REPCONF_OFF,
		REPCONF_ON
	};
	Tcl_Obj **myobjv, *onoff, *which;
	int myobjc, on, optindex, result, ret;
	u_int32_t wh;

	result = Tcl_ListObjGetElements(interp, list, &myobjc, &myobjv);
	if (result != TCL_OK)
		return (result);
	which = myobjv[0];
	onoff = myobjv[1];
	if (Tcl_GetIndexFromObjStruct(interp, which,
		&rep_config_types[0].name, sizeof(NAMEMAP),
		"config type", TCL_EXACT, &optindex) != TCL_OK)
		return (IS_HELP(which));

	wh = rep_config_types[optindex].value;
	if (Tcl_GetIndexFromObj(interp, onoff, confonoff, "option",
	    TCL_EXACT, &optindex) != TCL_OK)
		return (IS_HELP(onoff));
	switch ((enum confonoff)optindex) {
	case REPCONF_OFF:
		on = 0;
		break;
	case REPCONF_ON:
		on = 1;
		break;
	default:
		return (TCL_ERROR);
	}
	ret = dbenv->rep_set_config(dbenv, wh, on);
	return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret),
	    "env rep_config"));
}

/*
 * tcl_RepGetTwo --
 *	Call replication getters that return 2 values.
 *
 * PUBLIC: int tcl_RepGetTwo
 * PUBLIC:     __P((Tcl_Interp *, DB_ENV *, int));
 */
int
tcl_RepGetTwo(interp, dbenv, op)
	Tcl_Interp *interp;		/* Interpreter */
	DB_ENV *dbenv;			/* Environment pointer */
	int op;				/* which getter */
{
	Tcl_Obj *myobjv[2], *res;
	u_int32_t val1, val2;
	int myobjc, result, ret;

	ret = 0;
	val1 = val2 = 0;
	switch (op) {
	case DBTCL_GETCLOCK:
		ret = dbenv->rep_get_clockskew(dbenv, &val1, &val2);
		break;
	case DBTCL_GETLIMIT:
		ret = dbenv->rep_get_limit(dbenv, &val1, &val2);
		break;
	case DBTCL_GETREQ:
		ret = dbenv->rep_get_request(dbenv, &val1, &val2);
		break;
	default:
		return (TCL_ERROR);
	}
	if ((result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
	    "env rep_get")) == TCL_OK) {
		myobjc = 2;
		myobjv[0] = Tcl_NewLongObj((long)val1);
		myobjv[1] = Tcl_NewLongObj((long)val2);
		res = Tcl_NewListObj(myobjc, myobjv);
		Tcl_SetObjResult(interp, res);
	}
	return (result);
}

/*
 * tcl_RepGetConfig --
 *
 * PUBLIC: int tcl_RepGetConfig
 * PUBLIC:     __P((Tcl_Interp *, DB_ENV *, Tcl_Obj *));
 */
int
tcl_RepGetConfig(interp, dbenv, which)
	Tcl_Interp *interp;		/* Interpreter */
	DB_ENV *dbenv;			/* Environment pointer */
	Tcl_Obj *which;			/* which flag */
{
	Tcl_Obj *res;
	int on, optindex, result, ret;
	u_int32_t wh;

	if (Tcl_GetIndexFromObjStruct(interp, which,
	    &rep_config_types[0].name, sizeof(NAMEMAP),
	    "config type", TCL_EXACT, &optindex) != TCL_OK)
		return (IS_HELP(which));
	wh = rep_config_types[optindex].value;

	ret = dbenv->rep_get_config(dbenv, wh, &on);
	if ((result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
	    "env rep_config")) == TCL_OK) {
		res = Tcl_NewIntObj(on);
		Tcl_SetObjResult(interp, res);
	}
	return (result);
}

/*
 * tcl_RepGetTimeout --
 *	Get various replication timeout values.
 *
 * PUBLIC: int tcl_RepGetTimeout
 * PUBLIC:     __P((Tcl_Interp *, DB_ENV *, Tcl_Obj *));
 */
int
tcl_RepGetTimeout(interp, dbenv, which)
	Tcl_Interp *interp;		/* Interpreter */
	DB_ENV *dbenv;			/* Environment pointer */
	Tcl_Obj *which;			/* which flag */
{
	Tcl_Obj *res;
	int optindex, result, ret, wh;
	u_int32_t to;

	if (Tcl_GetIndexFromObjStruct(interp, which,
		&rep_timeout_types[0].name, sizeof(NAMEMAP),
		"timeout type", TCL_EXACT, &optindex) != TCL_OK)
		return (IS_HELP(which));
	wh = (int)rep_timeout_types[optindex].value;
	ret = dbenv->rep_get_timeout(dbenv, wh, &to);
	if ((result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
	    "env rep_get_timeout")) == TCL_OK) {
		res = Tcl_NewLongObj((long)to);
		Tcl_SetObjResult(interp, res);
	}
	return (result);
}

/*
 * tcl_RepGetAckPolicy
 *	Get Replication Manager acknowledgement policy 
 *
 * PUBLIC: int tcl_RepGetAckPolicy
 * PUBLIC:     __P((Tcl_Interp *, int, Tcl_Obj * CONST *, DB_ENV *));
 */
int
tcl_RepGetAckPolicy(interp, objc, objv, dbenv)
	Tcl_Interp *interp;
	int objc;
	Tcl_Obj *CONST objv[];
	DB_ENV *dbenv;
{
	const char *name;
	int policy, ret;

	if (objc != 2) {
		Tcl_WrongNumArgs(interp, 2, objv, "");
		return (TCL_ERROR);
	}

	if ((ret = dbenv->repmgr_get_ack_policy(dbenv, &policy)) != 0 ||
	    (ret = tcl_RepNumberToName(rep_ack_policies,
	    (u_int32_t)policy, &name)) != 0)
		return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			"env repmgr_get_ack_policy"));

	Tcl_SetObjResult(interp, NewStringObj(name, strlen(name)));
	return (TCL_OK);
}

/*
 * tcl_RepGetLocalSite
 *	Get local site address.
 *
 * PUBLIC: int tcl_RepGetLocalSite
 * PUBLIC:     __P((Tcl_Interp *, int, Tcl_Obj * CONST *, DB_ENV *));
 */
int
tcl_RepGetLocalSite(interp, objc, objv, dbenv)
	Tcl_Interp *interp;
	int objc;
	Tcl_Obj *CONST objv[];
	DB_ENV *dbenv;
{
	Tcl_Obj *myobjv[2];
	DB_SITE *dbsite;
	const char *host;
	int ret, t_ret;
	u_int port;

	if (objc != 2) {
		Tcl_WrongNumArgs(interp, 2, objv, "");
		return (TCL_ERROR);
	}

	if ((ret = dbenv->repmgr_local_site(dbenv, &dbsite)) == 0) {
		if ((ret = dbsite->get_address(dbsite, &host, &port)) == 0) {
			myobjv[0] = NewStringObj(host, strlen(host));
			myobjv[1] = Tcl_NewIntObj((int)port);
			Tcl_SetObjResult(interp, Tcl_NewListObj(2, myobjv));
		}
		if ((t_ret = dbsite->close(dbsite)) != 0 && ret == 0)
			ret = t_ret;
	}
	return (ret == 0 ? TCL_OK :
	    _ReturnSetup(interp, ret,
	    DB_RETOK_REPMGR_LOCALSITE(ret), "env repmgr_local_site"));
}




/*
 * tcl_RepNumberToName
 *	Map a #define'd int value to the corresponding name.
 */ 
static int
tcl_RepNumberToName(map, value, namep)
	const NAMEMAP *map;
	u_int32_t value;
	const char **namep;
{
	while (map->name) {
		if (map->value == value) {
			*namep = map->name;
			return (0);
		}
		map++;
	}
	return (DB_NOTFOUND);
}
#endif

#ifdef CONFIG_TEST
/*
 * tcl_RepElect --
 *	Call DB_ENV->rep_elect().
 *
 * PUBLIC: int tcl_RepElect
 * PUBLIC:     __P((Tcl_Interp *, int, Tcl_Obj * CONST *, DB_ENV *));
 */
int
tcl_RepElect(interp, objc, objv, dbenv)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
	DB_ENV *dbenv;			/* Environment pointer */
{
	int result, ret;
	u_int32_t full_timeout, nsites, nvotes, pri, timeout;

	if (objc != 6 && objc != 7) {
		Tcl_WrongNumArgs(interp, 2, objv,
		    "nsites nvotes pri timeout [full_timeout]");
		return (TCL_ERROR);
	}

	if ((result = _GetUInt32(interp, objv[2], &nsites)) != TCL_OK)
		return (result);
	if ((result = _GetUInt32(interp, objv[3], &nvotes)) != TCL_OK)
		return (result);
	if ((result = _GetUInt32(interp, objv[4], &pri)) != TCL_OK)
		return (result);
	if ((result = _GetUInt32(interp, objv[5], &timeout)) != TCL_OK)
		return (result);
	full_timeout = 0;
	if (objc == 7)
		if ((result = _GetUInt32(interp, objv[6], &full_timeout))
		    != TCL_OK)
			return (result);

	_debug_check();

	if ((ret = dbenv->rep_set_priority(dbenv, pri)) != 0)
		return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "env rep_elect (rep_set_priority)"));
	if ((ret = dbenv->rep_set_timeout(dbenv, DB_REP_ELECTION_TIMEOUT,
	    timeout)) != 0)
		return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "env rep_elect (rep_set_timeout)"));

	if (full_timeout != 0 && (ret = dbenv->rep_set_timeout(dbenv,
	    DB_REP_FULL_ELECTION_TIMEOUT, full_timeout)) != 0)
		return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "env rep_elect (rep_set_timeout)"));

	ret = dbenv->rep_elect(dbenv, nsites, nvotes, 0);
	return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret), "env rep_elect"));
}
#endif

#ifdef CONFIG_TEST
/*
 * tcl_RepFlush --
 *	Call DB_ENV->rep_flush().
 *
 * PUBLIC: int tcl_RepFlush
 * PUBLIC:     __P((Tcl_Interp *, int, Tcl_Obj * CONST *, DB_ENV *));
 */
int
tcl_RepFlush(interp, objc, objv, dbenv)
	Tcl_Interp *interp;
	int objc;
	Tcl_Obj *CONST objv[];
	DB_ENV *dbenv;
{
	int ret;

	if (objc != 2) {
		Tcl_WrongNumArgs(interp, 2, objv, "");
		return TCL_ERROR;
	}

	_debug_check();
	ret = dbenv->rep_flush(dbenv);
	return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret), "env rep_flush"));
}
#endif

#ifdef CONFIG_TEST
/*
 * tcl_RepSync --
 *	Call DB_ENV->rep_sync().
 *
 * PUBLIC: int tcl_RepSync
 * PUBLIC:     __P((Tcl_Interp *, int, Tcl_Obj * CONST *, DB_ENV *));
 */
int
tcl_RepSync(interp, objc, objv, dbenv)
	Tcl_Interp *interp;
	int objc;
	Tcl_Obj *CONST objv[];
	DB_ENV *dbenv;
{
	int ret;

	if (objc != 2) {
		Tcl_WrongNumArgs(interp, 2, objv, "");
		return TCL_ERROR;
	}

	_debug_check();
	ret = dbenv->rep_sync(dbenv, 0);
	return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret), "env rep_sync"));
}
#endif

#ifdef CONFIG_TEST
/*
 * tcl_RepLease --
 *	Call DB_ENV->rep_set_lease().
 *
 * PUBLIC: int tcl_RepLease  __P((Tcl_Interp *, int, Tcl_Obj * CONST *,
 * PUBLIC:    DB_ENV *));
 */
int
tcl_RepLease(interp, objc, objv, dbenv)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
	DB_ENV *dbenv;
{
	u_int32_t clock_fast, clock_slow, timeout;
	int result, ret;

	COMPQUIET(clock_fast, 0);
	COMPQUIET(clock_slow, 0);

	if (objc != 3 && objc != 1) {
		Tcl_WrongNumArgs(interp, 1, objv, "{timeout fast slow}");
		return (TCL_ERROR);
	}

	if ((result = _GetUInt32(interp, objv[0], &timeout)) != TCL_OK)
		return (result);
	if (objc == 4) {
		if ((result = _GetUInt32(interp, objv[1], &clock_fast))
		    != TCL_OK)
			return (result);
		if ((result = _GetUInt32(interp, objv[2], &clock_slow))
		    != TCL_OK)
			return (result);
	}
	ret = dbenv->rep_set_timeout(dbenv, DB_REP_LEASE_TIMEOUT,
	    (db_timeout_t)timeout);
	result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
	    "rep_set_timeout");
	ret = dbenv->rep_set_config(dbenv, DB_REP_CONF_LEASE, 1);
	result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
	    "rep_set_config");
	if (result != TCL_OK)
		return (result);
	if (objc == 3)
		ret = dbenv->rep_set_clockskew(dbenv, clock_fast, clock_slow);
	return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret),
	    "env rep_set_lease"));
}
#endif

#ifdef CONFIG_TEST
/*
 * tcl_RepInmemFiles --
 *	Set in-memory replication, which must be done before opening
 *	environment.
 *
 * PUBLIC: int tcl_RepInmemFiles  __P((Tcl_Interp *, DB_ENV *));
 */
int
tcl_RepInmemFiles(interp, dbenv)
	Tcl_Interp *interp;		/* Interpreter */
	DB_ENV *dbenv;
{
	int ret;

	ret = dbenv->rep_set_config(dbenv, DB_REP_CONF_INMEM, 1);
	return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret),
	    "rep_set_config"));
}
#endif

#ifdef CONFIG_TEST
/*
 * tcl_RepLimit --
 *	Call DB_ENV->rep_set_limit().
 *
 * PUBLIC: int tcl_RepLimit
 * PUBLIC:     __P((Tcl_Interp *, int, Tcl_Obj * CONST *, DB_ENV *));
 */
int
tcl_RepLimit(interp, objc, objv, dbenv)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
	DB_ENV *dbenv;			/* Environment pointer */
{
	int result, ret;
	u_int32_t bytes, gbytes;

	if (objc != 4) {
		Tcl_WrongNumArgs(interp, 2, objv, "gbytes bytes");
		return (TCL_ERROR);
	}

	if ((result = _GetUInt32(interp, objv[2], &gbytes)) != TCL_OK)
		return (result);
	if ((result = _GetUInt32(interp, objv[3], &bytes)) != TCL_OK)
		return (result);

	_debug_check();
	if ((ret = dbenv->rep_set_limit(dbenv, gbytes, bytes)) != 0)
		return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret),
		    "env set_rep_limit"));

	return (_ReturnSetup(interp,
	    ret, DB_RETOK_STD(ret), "env set_rep_limit"));
}
#endif

#ifdef CONFIG_TEST
/*
 * tcl_RepNSites --
 *	Call DB_ENV->rep_set_nsites().
 *
 * PUBLIC: int tcl_RepNSites
 * PUBLIC:     __P((Tcl_Interp *, int, Tcl_Obj * CONST *, DB_ENV *));
 */
int
tcl_RepNSites(interp, objc, objv, dbenv)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
	DB_ENV *dbenv;			/* Environment pointer */
{
	int result, ret;
	u_int32_t nsites;

	if (objc != 3) {
		Tcl_WrongNumArgs(interp, 2, objv, "nsites");
		return (TCL_ERROR);
	}

	if ((result = _GetUInt32(interp, objv[2], &nsites)) != TCL_OK)
		return (result);

	_debug_check();
	ret = dbenv->rep_set_nsites(dbenv, nsites);
	return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret), "env rep_nsites"));
}
#endif

#ifdef CONFIG_TEST
/*
 * tcl_RepRequest --
 *	Call DB_ENV->rep_set_request().
 *
 * PUBLIC: int tcl_RepRequest
 * PUBLIC:     __P((Tcl_Interp *, int, Tcl_Obj * CONST *, DB_ENV *));
 */
int
tcl_RepRequest(interp, objc, objv, dbenv)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
	DB_ENV *dbenv;			/* Environment pointer */
{
	int result, ret;
	long min, max;

	if (objc != 4) {
		Tcl_WrongNumArgs(interp, 2, objv, "min max");
		return (TCL_ERROR);
	}

	if ((result = Tcl_GetLongFromObj(interp, objv[2], &min)) != TCL_OK)
		return (result);
	if ((result = Tcl_GetLongFromObj(interp, objv[3], &max)) != TCL_OK)
		return (result);

	_debug_check();
	if ((ret = dbenv->rep_set_request(dbenv, (db_timeout_t)min,
	    (db_timeout_t)max)) != 0)
		return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret),
		    "env rep_request"));

	return (_ReturnSetup(interp,
	    ret, DB_RETOK_STD(ret), "env rep_request"));
}
#endif

#ifdef CONFIG_TEST
/*
 * tcl_RepNoarchiveTimeout --
 *	Reset the master update timer, to allow immediate log archiving.
 *
 * PUBLIC: int tcl_RepNoarchiveTimeout
 * PUBLIC:     __P((Tcl_Interp *, DB_ENV *));
 */
int
tcl_RepNoarchiveTimeout(interp, dbenv)
	Tcl_Interp *interp;		/* Interpreter */
	DB_ENV *dbenv;			/* Environment pointer */
{
	ENV *env;
	REGENV *renv;
	REGINFO *infop;

	env = dbenv->env;

	_debug_check();
	infop = env->reginfo;
	renv = infop->primary;
	REP_SYSTEM_LOCK(env);
	F_CLR(renv, DB_REGENV_REPLOCKED);
	renv->op_timestamp = 0;
	REP_SYSTEM_UNLOCK(env);

	return (_ReturnSetup(interp,
	    0, DB_RETOK_STD(0), "env test force noarchive_timeout"));
}
#endif

#ifdef CONFIG_TEST
/*
 * tcl_RepTransport --
 *	Call DB_ENV->rep_set_transport().
 *
 * PUBLIC: int tcl_RepTransport  __P((Tcl_Interp *, int, Tcl_Obj * CONST *,
 * PUBLIC:    DB_ENV *, DBTCL_INFO *));
 *
 *	Note that this normally can/should be achieved as an argument to
 * berkdb env, but we need to test changing the transport function on
 * the fly.
 */
int
tcl_RepTransport(interp, objc, objv, dbenv, ip)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
	DB_ENV *dbenv;
	DBTCL_INFO *ip;
{
	int intarg, result, ret;

	if (objc != 2) {
		Tcl_WrongNumArgs(interp, 2, objv, "{id transport_func}");
		return (TCL_ERROR);
	}

	/*
	 * Store the objects containing the machine ID
	 * and the procedure name.  We don't need to crack
	 * the send procedure out now, but we do convert the
	 * machine ID to an int, since rep_set_transport needs
	 * it.  Even so, it'll be easier later to deal with
	 * the Tcl_Obj *, so we save that, not the int.
	 *
	 * Note that we Tcl_IncrRefCount both objects
	 * independently;  Tcl is free to discard the list
	 * that they're bundled into.
	 */

	/*
	 * Check that the machine ID is an int.  Note that
	 * we do want to use GetIntFromObj;  the machine
	 * ID is explicitly an int, not a u_int32_t.
	 */
	if (ip->i_rep_eid != NULL) {
		Tcl_DecrRefCount(ip->i_rep_eid);
	}
	ip->i_rep_eid = objv[0];
	Tcl_IncrRefCount(ip->i_rep_eid);
	result = Tcl_GetIntFromObj(interp,
	    ip->i_rep_eid, &intarg);
	if (result != TCL_OK)
		return (result);

	if (ip->i_rep_send != NULL) {
		Tcl_DecrRefCount(ip->i_rep_send);
	}
	ip->i_rep_send = objv[1];
	Tcl_IncrRefCount(ip->i_rep_send);
	_debug_check();
	ret = dbenv->rep_set_transport(dbenv, intarg, tcl_rep_send);
	return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret),
	    "env rep_transport"));
}
#endif

#ifdef CONFIG_TEST
/*
 * tcl_RepStart --
 *	Call DB_ENV->rep_start().
 *
 * PUBLIC: int tcl_RepStart
 * PUBLIC:     __P((Tcl_Interp *, int, Tcl_Obj * CONST *, DB_ENV *));
 *
 *	Note that this normally can/should be achieved as an argument to
 * berkdb env, but we need to test forcible upgrading of clients, which
 * involves calling this on an open environment handle.
 */
int
tcl_RepStart(interp, objc, objv, dbenv)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
	DB_ENV *dbenv;
{
	static const char *tclrpstrt[] = {
		"-client",
		"-master",
		NULL
	};
	enum tclrpstrt {
		TCL_RPSTRT_CLIENT,
		TCL_RPSTRT_MASTER
	};
	char *arg;
	int i, optindex, ret;
	u_int32_t flag;

	flag = 0;

	if (objc != 3) {
		Tcl_WrongNumArgs(interp, 2, objv, "[-master/-client]");
		return (TCL_ERROR);
	}

	i = 2;
	while (i < objc) {
		if (Tcl_GetIndexFromObj(interp, objv[i], tclrpstrt,
		    "option", TCL_EXACT, &optindex) != TCL_OK) {
			arg = Tcl_GetStringFromObj(objv[i], NULL);
			if (arg[0] == '-')
				return (IS_HELP(objv[i]));
			else
				Tcl_ResetResult(interp);
			break;
		}
		i++;
		switch ((enum tclrpstrt)optindex) {
		case TCL_RPSTRT_CLIENT:
			flag = DB_REP_CLIENT;
			break;
		case TCL_RPSTRT_MASTER:
			flag = DB_REP_MASTER;
			break;
		}
	}

	_debug_check();
	ret = dbenv->rep_start(dbenv, NULL, flag);
	return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret), "env rep_start"));
}
#endif

#ifdef CONFIG_TEST
/*
 * tcl_RepProcessMessage --
 *	Call DB_ENV->rep_process_message().
 *
 * PUBLIC: int tcl_RepProcessMessage
 * PUBLIC:     __P((Tcl_Interp *, int, Tcl_Obj * CONST *, DB_ENV *));
 */
int
tcl_RepProcessMessage(interp, objc, objv, dbenv)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
	DB_ENV *dbenv;			/* Environment pointer */
{
	DBT control, rec;
	DB_LSN permlsn;
	Tcl_Obj *lsnlist, *myobjv[2], *res;
	void *ctmp, *rtmp;
	char *msg;
	int eid;
	int freectl, freerec, myobjc, result, ret;

	if (objc != 5) {
		Tcl_WrongNumArgs(interp, 2, objv, "id control rec");
		return (TCL_ERROR);
	}
	freectl = freerec = 0;

	memset(&control, 0, sizeof(control));
	memset(&rec, 0, sizeof(rec));

	if ((result = Tcl_GetIntFromObj(interp, objv[2], &eid)) != TCL_OK)
		return (result);

	ret = _CopyObjBytes(interp, objv[3], &ctmp,
	    &control.size, &freectl);
	if (ret != 0) {
		result = _ReturnSetup(interp, ret,
		    DB_RETOK_REPPMSG(ret), "rep_proc_msg");
		return (result);
	}
	control.data = ctmp;
	ret = _CopyObjBytes(interp, objv[4], &rtmp,
	    &rec.size, &freerec);
	if (ret != 0) {
		result = _ReturnSetup(interp, ret,
		    DB_RETOK_REPPMSG(ret), "rep_proc_msg");
		goto out;
	}
	rec.data = rtmp;
	_debug_check();
	ret = dbenv->rep_process_message(dbenv, &control, &rec, eid, &permlsn);
	/*
	 * !!!
	 * The TCL API diverges from the C++/Java APIs here.  For us, it
	 * is OK to get DUPMASTER and HOLDELECTION for testing purposes.
	 */
	result = _ReturnSetup(interp, ret,
	    DB_RETOK_REPPMSG(ret) || ret == DB_REP_DUPMASTER ||
	    ret == DB_REP_HOLDELECTION,
	    "env rep_process_message");

	if (result != TCL_OK)
		goto out;

	/*
	 * We have a valid return.  We need to return a variety of information.
	 * It will be one of the following:
	 * {0 0} -  Make a 0 return a list for consistent return structure.
	 * {DUPMASTER 0} -  DUPMASTER, no other info needed.
	 * {HOLDELECTION 0} -  HOLDELECTION, no other info needed.
	 * {NEWMASTER #} - NEWMASTER and its ID.
	 * {NEWSITE 0} - NEWSITE, no other info needed.
	 * {IGNORE {LSN list}} - IGNORE and this msg's LSN.
	 * {ISPERM {LSN list}} - ISPERM and the perm LSN.
	 * {NOTPERM {LSN list}} - NOTPERM and this msg's LSN.
	 */
	myobjc = 2;
	switch (ret) {
	case 0:
		myobjv[0] = Tcl_NewIntObj(0);
		myobjv[1] = Tcl_NewIntObj(0);
		break;
	case DB_REP_DUPMASTER:
		myobjv[0] = Tcl_NewByteArrayObj(
		    (u_char *)"DUPMASTER", (int)strlen("DUPMASTER"));
		myobjv[1] = Tcl_NewIntObj(0);
		break;
	case DB_REP_HOLDELECTION:
		myobjv[0] = Tcl_NewByteArrayObj(
		    (u_char *)"HOLDELECTION", (int)strlen("HOLDELECTION"));
		myobjv[1] = Tcl_NewIntObj(0);
		break;
	case DB_REP_IGNORE:
		myobjv[0] = Tcl_NewLongObj((long)permlsn.file);
		myobjv[1] = Tcl_NewLongObj((long)permlsn.offset);
		lsnlist = Tcl_NewListObj(myobjc, myobjv);
		myobjv[0] = Tcl_NewByteArrayObj(
		    (u_char *)"IGNORE", (int)strlen("IGNORE"));
		myobjv[1] = lsnlist;
		break;
	case DB_REP_ISPERM:
		myobjv[0] = Tcl_NewLongObj((long)permlsn.file);
		myobjv[1] = Tcl_NewLongObj((long)permlsn.offset);
		lsnlist = Tcl_NewListObj(myobjc, myobjv);
		myobjv[0] = Tcl_NewByteArrayObj(
		    (u_char *)"ISPERM", (int)strlen("ISPERM"));
		myobjv[1] = lsnlist;
		break;
	case DB_REP_NEWSITE:
		myobjv[0] = Tcl_NewByteArrayObj(
		    (u_char *)"NEWSITE", (int)strlen("NEWSITE"));
		myobjv[1] = Tcl_NewIntObj(0);
		break;
	case DB_REP_NOTPERM:
		myobjv[0] = Tcl_NewLongObj((long)permlsn.file);
		myobjv[1] = Tcl_NewLongObj((long)permlsn.offset);
		lsnlist = Tcl_NewListObj(myobjc, myobjv);
		myobjv[0] = Tcl_NewByteArrayObj(
		    (u_char *)"NOTPERM", (int)strlen("NOTPERM"));
		myobjv[1] = lsnlist;
		break;
	default:
		msg = db_strerror(ret);
		Tcl_AppendResult(interp, msg, NULL);
		Tcl_SetErrorCode(interp, "BerkeleyDB", msg, NULL);
		result = TCL_ERROR;
		goto out;
	}
	res = Tcl_NewListObj(myobjc, myobjv);
	if (res != NULL)
		Tcl_SetObjResult(interp, res);
out:
	if (freectl)
		__os_free(NULL, ctmp);
	if (freerec)
		__os_free(NULL, rtmp);

	return (result);
}
#endif

#ifdef CONFIG_TEST
/*
 * tcl_RepStat --
 *	Call DB_ENV->rep_stat().
 *
 * PUBLIC: int tcl_RepStat
 * PUBLIC:     __P((Tcl_Interp *, int, Tcl_Obj * CONST *, DB_ENV *));
 */
int
tcl_RepStat(interp, objc, objv, dbenv)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
	DB_ENV *dbenv;
{
	DB_REP_STAT *sp;
	Tcl_Obj *myobjv[2], *res, *thislist, *lsnlist;
	u_int32_t flag;
	int myobjc, result, ret;
	char *arg, *role;

	flag = 0;
	result = TCL_OK;

	if (objc > 3) {
		Tcl_WrongNumArgs(interp, 2, objv, NULL);
		return (TCL_ERROR);
	}
	if (objc == 3) {
		arg = Tcl_GetStringFromObj(objv[2], NULL);
		if (strcmp(arg, "-clear") == 0)
			flag = DB_STAT_CLEAR;
		else {
			Tcl_SetResult(interp,
			    "db stat: unknown arg", TCL_STATIC);
			return (TCL_ERROR);
		}
	}

	_debug_check();
	ret = dbenv->rep_stat(dbenv, &sp, flag);
	result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
	    "rep stat");
	if (result == TCL_ERROR)
		return (result);

	/*
	 * Have our stats, now construct the name value
	 * list pairs and free up the memory.
	 */
	res = Tcl_NewObj();
#ifdef HAVE_STATISTICS
	/*
	 * MAKE_STAT_* assumes 'res' and 'error' label.
	 */
	if (sp->st_status == DB_REP_MASTER)
		role = "master";
	else if (sp->st_status == DB_REP_CLIENT)
		role = "client";
	else
		role = "none";
	MAKE_STAT_STRLIST("Role", role);

	MAKE_STAT_LSN("Next LSN expected", &sp->st_next_lsn);
	MAKE_STAT_LSN("First missed LSN", &sp->st_waiting_lsn);
	MAKE_STAT_LSN("Maximum permanent LSN", &sp->st_max_perm_lsn);
	MAKE_WSTAT_LIST("Bulk buffer fills", sp->st_bulk_fills);
	MAKE_WSTAT_LIST("Bulk buffer overflows", sp->st_bulk_overflows);
	MAKE_WSTAT_LIST("Bulk records stored", sp->st_bulk_records);
	MAKE_WSTAT_LIST("Bulk buffer transfers", sp->st_bulk_transfers);
	MAKE_WSTAT_LIST("Client service requests", sp->st_client_svc_req);
	MAKE_WSTAT_LIST("Client service req misses", sp->st_client_svc_miss);
	MAKE_WSTAT_LIST("Client rerequests", sp->st_client_rerequests);
	MAKE_STAT_LIST("Duplicate master conditions", sp->st_dupmasters);
	MAKE_STAT_LIST("Environment ID", sp->st_env_id);
	MAKE_STAT_LIST("Environment priority", sp->st_env_priority);
	MAKE_STAT_LIST("Generation number", sp->st_gen);
	MAKE_STAT_LIST("Election generation number", sp->st_egen);
	MAKE_STAT_LIST("Startup complete", sp->st_startup_complete);
	MAKE_WSTAT_LIST("Lease messages sent", sp->st_lease_sends);
	MAKE_WSTAT_LIST("Lease checks", sp->st_lease_chk);
	MAKE_WSTAT_LIST("Lease check invalid", sp->st_lease_chk_misses);
	MAKE_WSTAT_LIST("Lease check refresh", sp->st_lease_chk_refresh);
	MAKE_WSTAT_LIST("Duplicate log records received", sp->st_log_duplicated);
	MAKE_WSTAT_LIST("Current log records queued", sp->st_log_queued);
	MAKE_WSTAT_LIST("Maximum log records queued", sp->st_log_queued_max);
	MAKE_WSTAT_LIST("Total log records queued", sp->st_log_queued_total);
	MAKE_WSTAT_LIST("Log records received", sp->st_log_records);
	MAKE_WSTAT_LIST("Log records requested", sp->st_log_requested);
	MAKE_STAT_LIST("Master environment ID", sp->st_master);
	MAKE_WSTAT_LIST("Master changes", sp->st_master_changes);
	MAKE_STAT_LIST("Messages with bad generation number",
	    sp->st_msgs_badgen);
	MAKE_WSTAT_LIST("Messages processed", sp->st_msgs_processed);
	MAKE_WSTAT_LIST("Messages ignored for recovery", sp->st_msgs_recover);
	MAKE_WSTAT_LIST("Message send failures", sp->st_msgs_send_failures);
	MAKE_WSTAT_LIST("Messages sent", sp->st_msgs_sent);
	MAKE_WSTAT_LIST("New site messages", sp->st_newsites);
	MAKE_STAT_LIST("Number of sites in replication group", sp->st_nsites);
	MAKE_WSTAT_LIST("Transmission limited", sp->st_nthrottles);
	MAKE_WSTAT_LIST("Outdated conditions", sp->st_outdated);
	MAKE_WSTAT_LIST("Transactions applied", sp->st_txns_applied);
	MAKE_STAT_LIST("Next page expected", sp->st_next_pg);
	MAKE_WSTAT_LIST("First missed page", sp->st_waiting_pg);
	MAKE_WSTAT_LIST("Duplicate pages received", sp->st_pg_duplicated);
	MAKE_WSTAT_LIST("Pages received", sp->st_pg_records);
	MAKE_WSTAT_LIST("Pages requested", sp->st_pg_requested);
	MAKE_WSTAT_LIST("Elections held", sp->st_elections);
	MAKE_WSTAT_LIST("Elections won", sp->st_elections_won);
	MAKE_STAT_LIST("Election phase", sp->st_election_status);
	MAKE_STAT_LIST("Election winner", sp->st_election_cur_winner);
	MAKE_STAT_LIST("Election winner generation number",
	    sp->st_election_gen);
	MAKE_STAT_LIST("Election data generation number", 
	    sp->st_election_datagen);
	MAKE_STAT_LSN("Election max LSN", &sp->st_election_lsn);
	MAKE_STAT_LIST("Election sites", sp->st_election_nsites);
	MAKE_STAT_LIST("Election nvotes", sp->st_election_nvotes);
	MAKE_STAT_LIST("Election priority", sp->st_election_priority);
	MAKE_STAT_LIST("Election tiebreaker", sp->st_election_tiebreaker);
	MAKE_STAT_LIST("Election votes", sp->st_election_votes);
	MAKE_STAT_LIST("Election seconds", sp->st_election_sec);
	MAKE_STAT_LIST("Election usecs", sp->st_election_usec);
	MAKE_STAT_LIST("Start-sync operations delayed",
	    sp->st_startsync_delayed);
	MAKE_STAT_LIST("Maximum lease seconds", sp->st_max_lease_sec);
	MAKE_STAT_LIST("Maximum lease usecs", sp->st_max_lease_usec);
	MAKE_STAT_LIST("File fail cleanups done", sp->st_filefail_cleanups);
#endif

	Tcl_SetObjResult(interp, res);
error:
	__os_ufree(dbenv->env, sp);
	return (result);
}

/*
 * tcl_RepStatPrint --
 *
 * PUBLIC: int tcl_RepStatPrint __P((Tcl_Interp *, int,
 * PUBLIC:    Tcl_Obj * CONST*, DB_ENV *));
 */
int
tcl_RepStatPrint(interp, objc, objv, dbenv)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
	DB_ENV *dbenv;			/* Environment pointer */
{	
	static const char *repstatprtopts[] = {
		"-all",
		"-clear",
		 NULL
	};
	enum repstatprtopts {
		REPSTATPRTALL,
		REPSTATPRTCLEAR
	};
	u_int32_t flag;
	int i, optindex, result, ret;

	result = TCL_OK;
	flag = 0;
	i = 2;

	while (i < objc) {
		if (Tcl_GetIndexFromObj(interp, objv[i], repstatprtopts, 
		    "option", TCL_EXACT, &optindex) != TCL_OK) {
			result = IS_HELP(objv[i]);
			goto error;
		}
		i++;
		switch ((enum repstatprtopts)optindex) {
		case REPSTATPRTALL:
			flag |= DB_STAT_ALL;
			break;
		case REPSTATPRTCLEAR:
			flag |= DB_STAT_CLEAR;
			break;
		}
		if (result != TCL_OK)
			break;
	}
	if (result != TCL_OK)
		goto error;

	_debug_check();
	ret = dbenv->rep_stat_print(dbenv, flag);
	result = _ReturnSetup(interp, 
	    ret, DB_RETOK_STD(ret), "dbenv rep_stat_print");
error:
	return (result);

}

/*
 * tcl_RepMgr --
 *	Configure and start the Replication Manager.
 *
 * PUBLIC: int tcl_RepMgr
 * PUBLIC:     __P((Tcl_Interp *, int, Tcl_Obj * CONST *, DB_ENV *));
 */
int
tcl_RepMgr(interp, objc, objv, dbenv)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
	DB_ENV *dbenv;			/* Environment pointer */
{
	static const char *rmgr[] = {
		"-ack",
		"-local",
		"-msgth",
		"-pri",
		"-remote",
		"-remove",
		"-start",
		"-timeout",
		NULL
	};
	enum rmgr {
		RMGR_ACK,
		RMGR_LOCAL,
		RMGR_MSGTH,
		RMGR_PRI,
		RMGR_REMOTE,
		RMGR_REMOVE,
		RMGR_START,
		RMGR_TIMEOUT
	};
	Tcl_Obj **myobjv;
	DB_SITE *dbsite;
	long to;
	int ack, creator, i, j, legacy, myobjc, optindex;
	int peer, result, ret, totype, t_ret;
	u_int32_t msgth, start_flag, uintarg;
	char *arg;

	result = TCL_OK;
	ack = ret = totype = 0;
	msgth = 1;
	start_flag = 0;

	if (objc <= 2) {
		Tcl_WrongNumArgs(interp, 2, objv, "?args?");
		return (TCL_ERROR);
	}
	/*
	 * Get the command name index from the object based on the bdbcmds
	 * defined above.
	 */
	i = 2;
	while (i < objc) {
		Tcl_ResetResult(interp);
		if (Tcl_GetIndexFromObj(interp, objv[i], rmgr, "option",
		    TCL_EXACT, &optindex) != TCL_OK) {
			result = IS_HELP(objv[i]);
			goto error;
		}
		i++;
		switch ((enum rmgr)optindex) {
		case RMGR_ACK:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-ack policy?");
				result = TCL_ERROR;
				break;
			}


			if (Tcl_GetIndexFromObjStruct(interp, objv[i++],
				&rep_ack_policies[0].name, sizeof(NAMEMAP),
				"ack policy", TCL_EXACT, &optindex) != TCL_OK) {
				result = TCL_ERROR;
				break;
			}
			ack = (int)rep_ack_policies[optindex].value;

			_debug_check();
			ret = dbenv->repmgr_set_ack_policy(dbenv, ack);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "ack");
			break;
		case RMGR_LOCAL:
			result = Tcl_ListObjGetElements(interp, objv[i],
			    &myobjc, &myobjv);
			if (result == TCL_OK)
				i++;
			else
				break;
			if (myobjc < 2 || myobjc > 4) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-local {host port [creator][legacy]}?");
				result = TCL_ERROR;
				break;
			}
			creator = legacy = 0;
			for (j = 2; j < myobjc; j++) {
				arg = Tcl_GetString(myobjv[j]);
				if (strcmp(arg, "creator") == 0)
					creator = 1;
				else if (strcmp(arg, "legacy") == 0)
					legacy = 1;
				else {
					Tcl_AddErrorInfo(interp,
					    "local: illegal flag");
					result = TCL_ERROR;
					break;
				}
			}
			/* The "arg" is host name, and "uintarg" is port. */
			arg = Tcl_GetStringFromObj(myobjv[0], NULL);
			if ((result = _GetUInt32(interp, myobjv[1], &uintarg))
			    != TCL_OK)
				break;
			_debug_check();
			if ((ret = dbenv->repmgr_site(dbenv,
			    arg, uintarg, &dbsite, 0)) == 0) {
				ret = dbsite->set_config(dbsite,
				    DB_LOCAL_SITE, 1);
				if (ret == 0 && creator)
					ret = dbsite->set_config(dbsite,
					    DB_GROUP_CREATOR, 1);
				if (ret == 0 && legacy)
					ret = dbsite->set_config(dbsite,
					    DB_LEGACY, 1);
				if ((t_ret = dbsite->close(dbsite)) != 0 &&
				    ret == 0)
					ret = t_ret;
			}
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "repmgr_site");
			break;
		case RMGR_MSGTH:
			if (i >= objc) {
				Tcl_WrongNumArgs(
				    interp, 2, objv, "?-msgth nth?");
				result = TCL_ERROR;
				break;
			}
			result = _GetUInt32(interp, objv[i++], &msgth);
			break;
		case RMGR_PRI:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-pri priority?");
				result = TCL_ERROR;
				break;
			}
			result = _GetUInt32(interp, objv[i++], &uintarg);
			if (result == TCL_OK) {
				_debug_check();
				ret = dbenv->
				    rep_set_priority(dbenv, uintarg);
			}
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "rep_set_priority");
			break;
		case RMGR_REMOTE:
			result = Tcl_ListObjGetElements(interp, objv[i],
			    &myobjc, &myobjv);
			if (result == TCL_OK)
				i++;
			else
				break;
			if (myobjc < 2 || myobjc > 4) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-remote {host port [peer][legacy]}?");
				result = TCL_ERROR;
				break;
			}

			/*
			 * Get the flags first so we can reuse 'arg'.
			 */
			legacy = peer = 0;
			for (j = 2; j < myobjc; j++) {
				arg = Tcl_GetString(myobjv[j]);
				if (strcmp(arg, "legacy") == 0)
					legacy = 1;
				else if (strcmp(arg, "peer") == 0)
					peer = 1;
				else {
					Tcl_AddErrorInfo(interp,
					    "remote: illegal flag");
					result = TCL_ERROR;
					break;
				}
			}
			arg = Tcl_GetString(myobjv[0]);
			if ((result = _GetUInt32(interp, myobjv[1], &uintarg))
			    != TCL_OK)
				break;
			_debug_check();

			/* For now, all "-remote" sites become helpers. */
			if ((ret = dbenv->repmgr_site(dbenv,
			    arg, uintarg, &dbsite, 0)) == 0) {
				ret = dbsite->set_config(dbsite,
				    DB_BOOTSTRAP_HELPER, 1);
				if (ret == 0 && legacy)
					ret = dbsite->set_config(dbsite,
					    DB_LEGACY, 1);
				if (ret == 0 && peer)
					ret = dbsite->set_config(dbsite,
					    DB_REPMGR_PEER, 1);
				if ((t_ret = dbsite->close(dbsite)) != 0 &&
				    ret == 0)
					ret = t_ret;
			}
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "repmgr_site");
			break;
		case RMGR_REMOVE:
			result = Tcl_ListObjGetElements(interp, objv[i],
			    &myobjc, &myobjv);
			if (result == TCL_OK)
				i++;
			else
				break;
			if (myobjc != 2) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-remove {host port}?");
				result = TCL_ERROR;
				break;
			}
			arg = Tcl_GetString(myobjv[0]);
			if ((result = _GetUInt32(interp, myobjv[1], &uintarg))
			    != TCL_OK)
				break;
			_debug_check();
			if ((ret = dbenv->repmgr_site(dbenv,
			    arg, uintarg, &dbsite, 0)) == 0)
				ret = dbsite->remove(dbsite);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "repmgr_site->remove");
			break;
		case RMGR_START:
			if (i >= objc) {
				Tcl_WrongNumArgs(
				    interp, 2, objv, "?-start state?");
				result = TCL_ERROR;
				break;
			}
			arg = Tcl_GetStringFromObj(objv[i++], NULL);
			if (strcmp(arg, "master") == 0)
				start_flag = DB_REP_MASTER;
			else if (strcmp(arg, "client") == 0)
				start_flag = DB_REP_CLIENT;
			else if (strcmp(arg, "elect") == 0)
				start_flag = DB_REP_ELECTION;
			else {
				Tcl_AddErrorInfo(
				    interp, "start: illegal state");
				result = TCL_ERROR;
				break;
			}
			/*
			 * Some config functions need to be called
			 * before repmgr_start.  So finish parsing all
			 * the args and call repmgr_start at the end.
			 */
			break;
		case RMGR_TIMEOUT:
			result = Tcl_ListObjGetElements(interp, objv[i],
			    &myobjc, &myobjv);
			if (result == TCL_OK)
				i++;
			else
				break;
			if (myobjc != 2) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-timeout {type to}?");
				result = TCL_ERROR;
				break;
			}
			if (Tcl_GetIndexFromObjStruct(interp, myobjv[0],
				&rep_timeout_types[0].name, sizeof(NAMEMAP),
				"timeout type", TCL_EXACT, &optindex) != TCL_OK) {
				result = TCL_ERROR;
				break;
			}
			totype = (int)rep_timeout_types[optindex].value;
			if ((result = Tcl_GetLongFromObj(
			   interp, myobjv[1], &to)) != TCL_OK)
				break;
			_debug_check();
			ret = dbenv->rep_set_timeout(dbenv, totype,
			    (db_timeout_t)to);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "rep_set_timeout");
			break;
		}
		/*
		 * If, at any time, parsing the args we get an error,
		 * bail out and return.
		 */
		if (result != TCL_OK)
			goto error;
	}
	/*
	 * Only call repmgr_start if needed.  The user may use this
	 * call just to reconfigure, change policy, etc.
	 */
	if (start_flag != 0 && result == TCL_OK) {
		_debug_check();
		ret = dbenv->repmgr_start(dbenv, (int)msgth, start_flag);
		result = _ReturnSetup(
		    interp, ret, DB_RETOK_REPMGR_START(ret), "repmgr_start");
	}
error:
	return (result);
}

/*
 * tcl_RepMgrSiteList --
 *	Call DB_ENV->repmgr_site_list().
 *
 * PUBLIC: int tcl_RepMgrSiteList
 * PUBLIC:     __P((Tcl_Interp *, int, Tcl_Obj * CONST *, DB_ENV *));
 */
int
tcl_RepMgrSiteList(interp, objc, objv, dbenv)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
	DB_ENV *dbenv;
{
	DB_REPMGR_SITE *sp;
	Tcl_Obj *myobjv[5], *res, *thislist;
	u_int count, i;
	char *pr, *st;
	int myobjc, result, ret;

	result = TCL_OK;

	if (objc > 2) {
		Tcl_WrongNumArgs(interp, 2, objv, NULL);
		return (TCL_ERROR);
	}

	_debug_check();
	ret = dbenv->repmgr_site_list(dbenv, &count, &sp);
	result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
	    "repmgr sitelist");
	if (result == TCL_ERROR)
		return (result);

	/*
	 * Have our sites, now construct the {eid host port status peer}
	 * tuples and free up the memory.
	 */
	res = Tcl_NewObj();

	for (i = 0; i < count; ++i) {
		/*
		 * MAKE_SITE_LIST assumes 'res' and 'error' label.
		 */
		if (sp[i].status == DB_REPMGR_CONNECTED)
			st = "connected";
		else if (sp[i].status == DB_REPMGR_DISCONNECTED)
			st = "disconnected";
		else
			st = "unknown";
		if (F_ISSET(&sp[i], DB_REPMGR_ISPEER))
			pr = "peer";
		else
			pr = "non-peer";
		MAKE_SITE_LIST(sp[i].eid, sp[i].host, sp[i].port, st, pr);
	}

	Tcl_SetObjResult(interp, res);
error:
	__os_ufree(dbenv->env, sp);
	return (result);
}

/*
 * tcl_RepMgrStat --
 *	Call DB_ENV->repmgr_stat().
 *
 * PUBLIC: int tcl_RepMgrStat
 * PUBLIC:     __P((Tcl_Interp *, int, Tcl_Obj * CONST *, DB_ENV *));
 */
int
tcl_RepMgrStat(interp, objc, objv, dbenv)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
	DB_ENV *dbenv;
{
	DB_REPMGR_STAT *sp;
	Tcl_Obj *res;
	u_int32_t flag;
	int result, ret;
	char *arg;

	flag = 0;
	result = TCL_OK;

	if (objc > 3) {
		Tcl_WrongNumArgs(interp, 2, objv, NULL);
		return (TCL_ERROR);
	}
	if (objc == 3) {
		arg = Tcl_GetStringFromObj(objv[2], NULL);
		if (strcmp(arg, "-clear") == 0)
			flag = DB_STAT_CLEAR;
		else {
			Tcl_SetResult(interp,
			    "db stat: unknown arg", TCL_STATIC);
			return (TCL_ERROR);
		}
	}

	_debug_check();
	ret = dbenv->repmgr_stat(dbenv, &sp, flag);
	result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
	    "repmgr stat");
	if (result == TCL_ERROR)
		return (result);

	/*
	 * Have our stats, now construct the name value
	 * list pairs and free up the memory.
	 */
	res = Tcl_NewObj();
#ifdef HAVE_STATISTICS
	/*
	 * MAKE_STAT_* assumes 'res' and 'error' label.
	 */
	MAKE_WSTAT_LIST("Acknowledgement failures", sp->st_perm_failed);
	MAKE_WSTAT_LIST("Messages delayed", sp->st_msgs_queued);
	MAKE_WSTAT_LIST("Messages discarded", sp->st_msgs_dropped);
	MAKE_WSTAT_LIST("Connections dropped", sp->st_connection_drop);
	MAKE_WSTAT_LIST("Failed re-connects", sp->st_connect_fail);
	MAKE_WSTAT_LIST("Election threads", sp->st_elect_threads);
	MAKE_WSTAT_LIST("Max elect threads", sp->st_max_elect_threads);
#endif

	Tcl_SetObjResult(interp, res);
error:
	__os_ufree(dbenv->env, sp);
	return (result);
}

/*
 * tcl_RepMgrStatPrint --
 *
 * PUBLIC: int tcl_RepMgrStatPrint __P((Tcl_Interp *, int,
 * PUBLIC:     Tcl_Obj * CONST*, DB_ENV *));
 */
int
tcl_RepMgrStatPrint(interp, objc, objv, dbenv)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
	DB_ENV *dbenv;			/* Environment pointer */
{	
	static const char *repmgrstatprtopts[] = {
		"-all",
		"-clear",
		 NULL
	};
	enum repmgrstatprtopts {
		REPMGRSTATPRTALL,
		REPMGRSTATPRTCLEAR
	};
	u_int32_t flag;
	int i, optindex, result, ret;

	result = TCL_OK;
	flag = 0;
	i = 2;

	while (i < objc) {
		if (Tcl_GetIndexFromObj(interp, objv[i], repmgrstatprtopts, 
		    "option", TCL_EXACT, &optindex) != TCL_OK) {
			result = IS_HELP(objv[i]);
			goto error;
		}
		i++;
		switch ((enum repmgrstatprtopts)optindex) {
		case REPMGRSTATPRTALL:
			flag |= DB_STAT_ALL;
			break;
		case REPMGRSTATPRTCLEAR:
			flag |= DB_STAT_CLEAR;
			break;
		}
		if (result != TCL_OK)
			break;
	}
	if (result != TCL_OK)
		goto error;

	_debug_check();
	ret = dbenv->repmgr_stat_print(dbenv, flag);
	result = _ReturnSetup(interp, 
	    ret, DB_RETOK_STD(ret), "dbenv repmgr_stat_print");
error:
	return (result);

}

/*
 * tcl_RepApplied -
 *
 * PUBLIC: int tcl_RepApplied
 * PUBLIC:     __P((Tcl_Interp *, int, Tcl_Obj * CONST *, DB_ENV *));
 */
int
tcl_RepApplied(interp, objc, objv, dbenv)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
	DB_ENV *dbenv;
{
	static const char *repapplied_option_names[] = {
		"-timeout",
		NULL
	};
	enum envinfo_options {
		REPAPPLIEDTIMEOUT
	};
	unsigned char *arg;
	char msg[MSG_SIZE];
	db_timeout_t timeout;
	int i, len, ptr, result, ret;

	if (objc != 3 && objc != 5) {
		Tcl_WrongNumArgs(interp, 2, objv,
		    "?-timeout t? token");
		return (TCL_ERROR);
	}
	timeout = 0;
	i = 2;
	if (objc == 5) {
		if (Tcl_GetIndexFromObj(interp, objv[i],
		    repapplied_option_names, "option", TCL_EXACT, &ptr)
		    != TCL_OK)
			return (IS_HELP(objv[i]));
		i++;
		switch ((enum envinfo_options)ptr) {
		case REPAPPLIEDTIMEOUT:
			result = _GetUInt32(interp, objv[i++], &timeout);
			if (result != TCL_OK)
				return (result);
			break;
		}
	}

	arg = Tcl_GetByteArrayFromObj(objv[i], &len);
	if (len != DB_TXN_TOKEN_SIZE) {
		Tcl_SetErrorCode(interp, "BerkeleyDB",
		    "Commit token is the wrong size", NULL);

		snprintf(msg, MSG_SIZE,
		    "Bad commit token size %lu, should be %lu",
		    (u_long)len, (u_long)DB_TXN_TOKEN_SIZE);
		Tcl_SetResult(interp, msg, TCL_VOLATILE);
		return (TCL_ERROR);
	}		

	_debug_check();
	ret = dbenv->txn_applied(dbenv, (DB_TXN_TOKEN*)arg, timeout, 0);
	result = _ReturnSetup(interp, ret , DB_RETOK_TXNAPPLIED(ret),
	    "txn_applied");
	return (result);
}
#endif
