1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1999,2008 Oracle.  All rights reserved.
5 *
6 * $Id: tcl_rep.c,v 12.53 2008/02/05 13:00:22 sue Exp $
7 */
8
9#include "db_config.h"
10
11#include "db_int.h"
12#ifdef HAVE_SYSTEM_INCLUDE_FILES
13#include <tcl.h>
14#endif
15#include "dbinc/tcl_db.h"
16
17#ifdef CONFIG_TEST
18/*
19 * tcl_RepConfig --
20 *	Call DB_ENV->rep_set_config().
21 *
22 * PUBLIC: int tcl_RepConfig
23 * PUBLIC:     __P((Tcl_Interp *, DB_ENV *, Tcl_Obj *));
24 */
25int
26tcl_RepConfig(interp, dbenv, list)
27	Tcl_Interp *interp;		/* Interpreter */
28	DB_ENV *dbenv;			/* Environment pointer */
29	Tcl_Obj *list;			/* {which on|off} */
30{
31	static const char *confwhich[] = {
32		"bulk",
33		"delayclient",
34		"noautoinit",
35		"nowait",
36		NULL
37	};
38	enum confwhich {
39		REPCONF_BULK,
40		REPCONF_DELAYCLIENT,
41		REPCONF_NOAUTOINIT,
42		REPCONF_NOWAIT
43	};
44	static const char *confonoff[] = {
45		"off",
46		"on",
47		NULL
48	};
49	enum confonoff {
50		REPCONF_OFF,
51		REPCONF_ON
52	};
53	Tcl_Obj **myobjv, *onoff, *which;
54	int myobjc, on, optindex, result, ret;
55	u_int32_t wh;
56
57	result = Tcl_ListObjGetElements(interp, list, &myobjc, &myobjv);
58	which = myobjv[0];
59	onoff = myobjv[1];
60	if (result != TCL_OK)
61		return (result);
62	if (Tcl_GetIndexFromObj(interp, which, confwhich, "option",
63	    TCL_EXACT, &optindex) != TCL_OK)
64		return (IS_HELP(which));
65
66	switch ((enum confwhich)optindex) {
67	case REPCONF_NOAUTOINIT:
68		wh = DB_REP_CONF_NOAUTOINIT;
69		break;
70	case REPCONF_BULK:
71		wh = DB_REP_CONF_BULK;
72		break;
73	case REPCONF_DELAYCLIENT:
74		wh = DB_REP_CONF_DELAYCLIENT;
75		break;
76	case REPCONF_NOWAIT:
77		wh = DB_REP_CONF_NOWAIT;
78		break;
79	default:
80		return (TCL_ERROR);
81	}
82	if (Tcl_GetIndexFromObj(interp, onoff, confonoff, "option",
83	    TCL_EXACT, &optindex) != TCL_OK)
84		return (IS_HELP(onoff));
85	switch ((enum confonoff)optindex) {
86	case REPCONF_OFF:
87		on = 0;
88		break;
89	case REPCONF_ON:
90		on = 1;
91		break;
92	default:
93		return (TCL_ERROR);
94	}
95	ret = dbenv->rep_set_config(dbenv, wh, on);
96	return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret),
97	    "env rep_config"));
98}
99
100/*
101 * tcl_RepGetTwo --
102 *	Call replication getters that return 2 values.
103 *
104 * PUBLIC: int tcl_RepGetTwo
105 * PUBLIC:     __P((Tcl_Interp *, DB_ENV *, int));
106 */
107int
108tcl_RepGetTwo(interp, dbenv, op)
109	Tcl_Interp *interp;		/* Interpreter */
110	DB_ENV *dbenv;			/* Environment pointer */
111	int op;				/* which getter */
112{
113	Tcl_Obj *myobjv[2], *res;
114	u_int32_t val1, val2;
115	int myobjc, result, ret;
116
117	ret = 0;
118	val1 = val2 = 0;
119	switch (op) {
120	case DBTCL_GETCLOCK:
121		ret = dbenv->rep_get_clockskew(dbenv, &val1, &val2);
122		break;
123	case DBTCL_GETLIMIT:
124		ret = dbenv->rep_get_limit(dbenv, &val1, &val2);
125		break;
126	case DBTCL_GETREQ:
127		ret = dbenv->rep_get_request(dbenv, &val1, &val2);
128		break;
129	default:
130		return (TCL_ERROR);
131	}
132	if ((result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
133	    "env rep_get")) == TCL_OK) {
134		myobjc = 2;
135		myobjv[0] = Tcl_NewLongObj((long)val1);
136		myobjv[1] = Tcl_NewLongObj((long)val2);
137		res = Tcl_NewListObj(myobjc, myobjv);
138		Tcl_SetObjResult(interp, res);
139	}
140	return (result);
141}
142
143/*
144 * tcl_RepGetConfig --
145 *
146 * PUBLIC: int tcl_RepGetConfig
147 * PUBLIC:     __P((Tcl_Interp *, DB_ENV *, Tcl_Obj *));
148 */
149int
150tcl_RepGetConfig(interp, dbenv, which)
151	Tcl_Interp *interp;		/* Interpreter */
152	DB_ENV *dbenv;			/* Environment pointer */
153	Tcl_Obj *which;			/* which flag */
154{
155	static const char *confwhich[] = {
156		"bulk",
157		"delayclient",
158		"lease",
159		"noautoinit",
160		"nowait",
161		NULL
162	};
163	enum confwhich {
164		REPGCONF_BULK,
165		REPGCONF_DELAYCLIENT,
166		REPGCONF_LEASE,
167		REPGCONF_NOAUTOINIT,
168		REPGCONF_NOWAIT
169	};
170	Tcl_Obj *res;
171	int on, optindex, result, ret;
172	u_int32_t wh;
173
174	if (Tcl_GetIndexFromObj(interp, which, confwhich, "option",
175	    TCL_EXACT, &optindex) != TCL_OK)
176		return (IS_HELP(which));
177
178	res = NULL;
179	switch ((enum confwhich)optindex) {
180	case REPGCONF_BULK:
181		wh = DB_REP_CONF_BULK;
182		break;
183	case REPGCONF_DELAYCLIENT:
184		wh = DB_REP_CONF_DELAYCLIENT;
185		break;
186	case REPGCONF_LEASE:
187		wh = DB_REP_CONF_LEASE;
188		break;
189	case REPGCONF_NOAUTOINIT:
190		wh = DB_REP_CONF_NOAUTOINIT;
191		break;
192	case REPGCONF_NOWAIT:
193		wh = DB_REP_CONF_NOWAIT;
194		break;
195	default:
196		return (TCL_ERROR);
197	}
198	ret = dbenv->rep_get_config(dbenv, wh, &on);
199	if ((result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
200	    "env rep_config")) == TCL_OK) {
201		res = Tcl_NewIntObj(on);
202		Tcl_SetObjResult(interp, res);
203	}
204	return (result);
205}
206
207/*
208 * tcl_RepGetTimeout --
209 *	Get various replication timeout values.
210 *
211 * PUBLIC: int tcl_RepGetTimeout
212 * PUBLIC:     __P((Tcl_Interp *, DB_ENV *, Tcl_Obj *));
213 */
214int
215tcl_RepGetTimeout(interp, dbenv, which)
216	Tcl_Interp *interp;		/* Interpreter */
217	DB_ENV *dbenv;			/* Environment pointer */
218	Tcl_Obj *which;			/* which flag */
219{
220	static const char *towhich[] = {
221		"ack",
222		"checkpoint_delay",
223		"connection_retry",
224		"election",
225		"election_retry",
226		"full_election",
227		"heartbeat_monitor",
228		"heartbeat_send",
229		"lease",
230		NULL
231	};
232	enum towhich {
233		REPGTO_ACK,
234		REPGTO_CKP,
235		REPGTO_CONN,
236		REPGTO_ELECT,
237		REPGTO_ELECT_RETRY,
238		REPGTO_FULL,
239		REPGTO_HB_MON,
240		REPGTO_HB_SEND,
241		REPGTO_LEASE
242	};
243	Tcl_Obj *res;
244	int optindex, result, ret, wh;
245	u_int32_t to;
246
247	if (Tcl_GetIndexFromObj(interp, which, towhich, "option",
248	    TCL_EXACT, &optindex) != TCL_OK)
249		return (IS_HELP(which));
250
251	res = NULL;
252	switch ((enum towhich)optindex) {
253	case REPGTO_ACK:
254		wh = DB_REP_ACK_TIMEOUT;
255		break;
256	case REPGTO_CKP:
257		wh = DB_REP_CHECKPOINT_DELAY;
258		break;
259	case REPGTO_CONN:
260		wh = DB_REP_CONNECTION_RETRY;
261		break;
262	case REPGTO_ELECT:
263		wh = DB_REP_ELECTION_TIMEOUT;
264		break;
265	case REPGTO_ELECT_RETRY:
266		wh = DB_REP_ELECTION_RETRY;
267		break;
268	case REPGTO_FULL:
269		wh = DB_REP_FULL_ELECTION_TIMEOUT;
270		break;
271	case REPGTO_HB_MON:
272		wh = DB_REP_HEARTBEAT_MONITOR;
273		break;
274	case REPGTO_HB_SEND:
275		wh = DB_REP_HEARTBEAT_SEND;
276		break;
277	case REPGTO_LEASE:
278		wh = DB_REP_LEASE_TIMEOUT;
279		break;
280	default:
281		return (TCL_ERROR);
282	}
283	ret = dbenv->rep_get_timeout(dbenv, wh, &to);
284	if ((result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
285	    "env rep_config")) == TCL_OK) {
286		res = Tcl_NewLongObj((long)to);
287		Tcl_SetObjResult(interp, res);
288	}
289	return (result);
290}
291#endif
292
293#ifdef CONFIG_TEST
294/*
295 * tcl_RepElect --
296 *	Call DB_ENV->rep_elect().
297 *
298 * PUBLIC: int tcl_RepElect
299 * PUBLIC:     __P((Tcl_Interp *, int, Tcl_Obj * CONST *, DB_ENV *));
300 */
301int
302tcl_RepElect(interp, objc, objv, dbenv)
303	Tcl_Interp *interp;		/* Interpreter */
304	int objc;			/* How many arguments? */
305	Tcl_Obj *CONST objv[];		/* The argument objects */
306	DB_ENV *dbenv;			/* Environment pointer */
307{
308	int result, ret;
309	u_int32_t full_timeout, nsites, nvotes, pri, timeout;
310
311	if (objc != 6 && objc != 7) {
312		Tcl_WrongNumArgs(interp, 6, objv,
313		    "nsites nvotes pri timeout [full_timeout]");
314		return (TCL_ERROR);
315	}
316
317	if ((result = _GetUInt32(interp, objv[2], &nsites)) != TCL_OK)
318		return (result);
319	if ((result = _GetUInt32(interp, objv[3], &nvotes)) != TCL_OK)
320		return (result);
321	if ((result = _GetUInt32(interp, objv[4], &pri)) != TCL_OK)
322		return (result);
323	if ((result = _GetUInt32(interp, objv[5], &timeout)) != TCL_OK)
324		return (result);
325	full_timeout = 0;
326	if (objc == 7)
327		if ((result = _GetUInt32(interp, objv[6], &full_timeout))
328		    != TCL_OK)
329			return (result);
330
331	_debug_check();
332
333	if ((ret = dbenv->rep_set_priority(dbenv, pri)) != 0)
334		return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret),
335			    "env rep_elect (rep_set_priority)"));
336	if ((ret = dbenv->rep_set_timeout(dbenv, DB_REP_ELECTION_TIMEOUT,
337	    timeout)) != 0)
338		return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret),
339			    "env rep_elect (rep_set_timeout)"));
340
341	if (full_timeout != 0 && (ret = dbenv->rep_set_timeout(dbenv,
342	    DB_REP_FULL_ELECTION_TIMEOUT, full_timeout)) != 0)
343		return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret),
344			    "env rep_elect (rep_set_timeout)"));
345
346	ret = dbenv->rep_elect(dbenv, nsites, nvotes, 0);
347	return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret), "env rep_elect"));
348}
349#endif
350
351#ifdef CONFIG_TEST
352/*
353 * tcl_RepFlush --
354 *	Call DB_ENV->rep_flush().
355 *
356 * PUBLIC: int tcl_RepFlush
357 * PUBLIC:     __P((Tcl_Interp *, int, Tcl_Obj * CONST *, DB_ENV *));
358 */
359int
360tcl_RepFlush(interp, objc, objv, dbenv)
361	Tcl_Interp *interp;
362	int objc;
363	Tcl_Obj *CONST objv[];
364	DB_ENV *dbenv;
365{
366	int ret;
367
368	if (objc != 2) {
369		Tcl_WrongNumArgs(interp, 2, objv, "");
370		return TCL_ERROR;
371	}
372
373	_debug_check();
374	ret = dbenv->rep_flush(dbenv);
375	return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret), "env rep_flush"));
376}
377#endif
378
379#ifdef CONFIG_TEST
380/*
381 * tcl_RepSync --
382 *	Call DB_ENV->rep_sync().
383 *
384 * PUBLIC: int tcl_RepSync
385 * PUBLIC:     __P((Tcl_Interp *, int, Tcl_Obj * CONST *, DB_ENV *));
386 */
387int
388tcl_RepSync(interp, objc, objv, dbenv)
389	Tcl_Interp *interp;
390	int objc;
391	Tcl_Obj *CONST objv[];
392	DB_ENV *dbenv;
393{
394	int ret;
395
396	if (objc != 2) {
397		Tcl_WrongNumArgs(interp, 2, objv, "");
398		return TCL_ERROR;
399	}
400
401	_debug_check();
402	ret = dbenv->rep_sync(dbenv, 0);
403	return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret), "env rep_sync"));
404}
405#endif
406
407#ifdef CONFIG_TEST
408/*
409 * tcl_RepLease --
410 *	Call DB_ENV->rep_set_lease().
411 *
412 * PUBLIC: int tcl_RepLease  __P((Tcl_Interp *, int, Tcl_Obj * CONST *,
413 * PUBLIC:    DB_ENV *));
414 */
415int
416tcl_RepLease(interp, objc, objv, dbenv)
417	Tcl_Interp *interp;		/* Interpreter */
418	int objc;			/* How many arguments? */
419	Tcl_Obj *CONST objv[];		/* The argument objects */
420	DB_ENV *dbenv;
421{
422	u_int32_t clock_fast, clock_slow, nsites, timeout;
423	int result, ret;
424
425	COMPQUIET(clock_fast, 0);
426	COMPQUIET(clock_slow, 0);
427
428	if (objc != 4 && objc != 2) {
429		Tcl_WrongNumArgs(interp, 1, objv, "{nsites timeout fast slow}");
430		return (TCL_ERROR);
431	}
432
433	if ((result = _GetUInt32(interp, objv[0], &nsites)) != TCL_OK)
434		return (result);
435	if ((result = _GetUInt32(interp, objv[1], &timeout)) != TCL_OK)
436		return (result);
437	if (objc == 4) {
438		if ((result = _GetUInt32(interp, objv[2], &clock_fast))
439		    != TCL_OK)
440			return (result);
441		if ((result = _GetUInt32(interp, objv[3], &clock_slow))
442		    != TCL_OK)
443			return (result);
444	}
445	ret = dbenv->rep_set_nsites(dbenv, nsites);
446	result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
447	    "rep_set_nsites");
448	if (result != TCL_OK)
449		return (result);
450	ret = dbenv->rep_set_timeout(dbenv, DB_REP_LEASE_TIMEOUT,
451	    (db_timeout_t)timeout);
452	result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
453	    "rep_set_timeout");
454	ret = dbenv->rep_set_config(dbenv, DB_REP_CONF_LEASE, 1);
455	result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
456	    "rep_set_config");
457	if (result != TCL_OK)
458		return (result);
459	if (objc == 4)
460		ret = dbenv->rep_set_clockskew(dbenv, clock_fast, clock_slow);
461	return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret),
462	    "env rep_set_lease"));
463}
464#endif
465
466#ifdef CONFIG_TEST
467/*
468 * tcl_RepLimit --
469 *	Call DB_ENV->rep_set_limit().
470 *
471 * PUBLIC: int tcl_RepLimit
472 * PUBLIC:     __P((Tcl_Interp *, int, Tcl_Obj * CONST *, DB_ENV *));
473 */
474int
475tcl_RepLimit(interp, objc, objv, dbenv)
476	Tcl_Interp *interp;		/* Interpreter */
477	int objc;			/* How many arguments? */
478	Tcl_Obj *CONST objv[];		/* The argument objects */
479	DB_ENV *dbenv;			/* Environment pointer */
480{
481	int result, ret;
482	u_int32_t bytes, gbytes;
483
484	if (objc != 4) {
485		Tcl_WrongNumArgs(interp, 4, objv, "gbytes bytes");
486		return (TCL_ERROR);
487	}
488
489	if ((result = _GetUInt32(interp, objv[2], &gbytes)) != TCL_OK)
490		return (result);
491	if ((result = _GetUInt32(interp, objv[3], &bytes)) != TCL_OK)
492		return (result);
493
494	_debug_check();
495	if ((ret = dbenv->rep_set_limit(dbenv, gbytes, bytes)) != 0)
496		return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret),
497		    "env set_rep_limit"));
498
499	return (_ReturnSetup(interp,
500	    ret, DB_RETOK_STD(ret), "env set_rep_limit"));
501}
502#endif
503
504#ifdef CONFIG_TEST
505/*
506 * tcl_RepRequest --
507 *	Call DB_ENV->rep_set_request().
508 *
509 * PUBLIC: int tcl_RepRequest
510 * PUBLIC:     __P((Tcl_Interp *, int, Tcl_Obj * CONST *, DB_ENV *));
511 */
512int
513tcl_RepRequest(interp, objc, objv, dbenv)
514	Tcl_Interp *interp;		/* Interpreter */
515	int objc;			/* How many arguments? */
516	Tcl_Obj *CONST objv[];		/* The argument objects */
517	DB_ENV *dbenv;			/* Environment pointer */
518{
519	int result, ret;
520	long min, max;
521
522	if (objc != 4) {
523		Tcl_WrongNumArgs(interp, 4, objv, "min max");
524		return (TCL_ERROR);
525	}
526
527	if ((result = Tcl_GetLongFromObj(interp, objv[2], &min)) != TCL_OK)
528		return (result);
529	if ((result = Tcl_GetLongFromObj(interp, objv[3], &max)) != TCL_OK)
530		return (result);
531
532	_debug_check();
533	if ((ret = dbenv->rep_set_request(dbenv, (db_timeout_t)min,
534	    (db_timeout_t)max)) != 0)
535		return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret),
536		    "env rep_request"));
537
538	return (_ReturnSetup(interp,
539	    ret, DB_RETOK_STD(ret), "env rep_request"));
540}
541#endif
542
543#ifdef CONFIG_TEST
544/*
545 * tcl_RepNoarchiveTimeout --
546 *	Reset the master update timer, to allow immediate log archiving.
547 *
548 * PUBLIC: int tcl_RepNoarchiveTimeout
549 * PUBLIC:     __P((Tcl_Interp *, DB_ENV *));
550 */
551int
552tcl_RepNoarchiveTimeout(interp, dbenv)
553	Tcl_Interp *interp;		/* Interpreter */
554	DB_ENV *dbenv;			/* Environment pointer */
555{
556	ENV *env;
557	REGENV *renv;
558	REGINFO *infop;
559
560	env = dbenv->env;
561
562	_debug_check();
563	infop = env->reginfo;
564	renv = infop->primary;
565	REP_SYSTEM_LOCK(env);
566	F_CLR(renv, DB_REGENV_REPLOCKED);
567	renv->op_timestamp = 0;
568	REP_SYSTEM_UNLOCK(env);
569
570	return (_ReturnSetup(interp,
571	    0, DB_RETOK_STD(0), "env test force noarchive_timeout"));
572}
573#endif
574
575#ifdef CONFIG_TEST
576/*
577 * tcl_RepTransport --
578 *	Call DB_ENV->rep_set_transport().
579 *
580 * PUBLIC: int tcl_RepTransport  __P((Tcl_Interp *, int, Tcl_Obj * CONST *,
581 * PUBLIC:    DB_ENV *, DBTCL_INFO *));
582 *
583 *	Note that this normally can/should be achieved as an argument to
584 * berkdb env, but we need to test changing the transport function on
585 * the fly.
586 */
587int
588tcl_RepTransport(interp, objc, objv, dbenv, ip)
589	Tcl_Interp *interp;		/* Interpreter */
590	int objc;			/* How many arguments? */
591	Tcl_Obj *CONST objv[];		/* The argument objects */
592	DB_ENV *dbenv;
593	DBTCL_INFO *ip;
594{
595	int intarg, result, ret;
596
597	if (objc != 2) {
598		Tcl_WrongNumArgs(interp, 2, objv, "{id transport_func");
599		return (TCL_ERROR);
600	}
601
602	/*
603	 * Store the objects containing the machine ID
604	 * and the procedure name.  We don't need to crack
605	 * the send procedure out now, but we do convert the
606	 * machine ID to an int, since rep_set_transport needs
607	 * it.  Even so, it'll be easier later to deal with
608	 * the Tcl_Obj *, so we save that, not the int.
609	 *
610	 * Note that we Tcl_IncrRefCount both objects
611	 * independently;  Tcl is free to discard the list
612	 * that they're bundled into.
613	 */
614
615	/*
616	 * Check that the machine ID is an int.  Note that
617	 * we do want to use GetIntFromObj;  the machine
618	 * ID is explicitly an int, not a u_int32_t.
619	 */
620	if (ip->i_rep_eid != NULL) {
621		Tcl_DecrRefCount(ip->i_rep_eid);
622	}
623	ip->i_rep_eid = objv[0];
624	Tcl_IncrRefCount(ip->i_rep_eid);
625	result = Tcl_GetIntFromObj(interp,
626	    ip->i_rep_eid, &intarg);
627	if (result != TCL_OK)
628		return (result);
629
630	if (ip->i_rep_send != NULL) {
631		Tcl_DecrRefCount(ip->i_rep_send);
632	}
633	ip->i_rep_send = objv[1];
634	Tcl_IncrRefCount(ip->i_rep_send);
635	_debug_check();
636	ret = dbenv->rep_set_transport(dbenv, intarg, tcl_rep_send);
637	return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret),
638	    "env rep_transport"));
639}
640#endif
641
642#ifdef CONFIG_TEST
643/*
644 * tcl_RepStart --
645 *	Call DB_ENV->rep_start().
646 *
647 * PUBLIC: int tcl_RepStart
648 * PUBLIC:     __P((Tcl_Interp *, int, Tcl_Obj * CONST *, DB_ENV *));
649 *
650 *	Note that this normally can/should be achieved as an argument to
651 * berkdb env, but we need to test forcible upgrading of clients, which
652 * involves calling this on an open environment handle.
653 */
654int
655tcl_RepStart(interp, objc, objv, dbenv)
656	Tcl_Interp *interp;		/* Interpreter */
657	int objc;			/* How many arguments? */
658	Tcl_Obj *CONST objv[];		/* The argument objects */
659	DB_ENV *dbenv;
660{
661	static const char *tclrpstrt[] = {
662		"-client",
663		"-master",
664		NULL
665	};
666	enum tclrpstrt {
667		TCL_RPSTRT_CLIENT,
668		TCL_RPSTRT_MASTER
669	};
670	char *arg;
671	int i, optindex, ret;
672	u_int32_t flag;
673
674	flag = 0;
675
676	if (objc != 3) {
677		Tcl_WrongNumArgs(interp, 3, objv, "[-master/-client]");
678		return (TCL_ERROR);
679	}
680
681	i = 2;
682	while (i < objc) {
683		if (Tcl_GetIndexFromObj(interp, objv[i], tclrpstrt,
684		    "option", TCL_EXACT, &optindex) != TCL_OK) {
685			arg = Tcl_GetStringFromObj(objv[i], NULL);
686			if (arg[0] == '-')
687				return (IS_HELP(objv[i]));
688			else
689				Tcl_ResetResult(interp);
690			break;
691		}
692		i++;
693		switch ((enum tclrpstrt)optindex) {
694		case TCL_RPSTRT_CLIENT:
695			flag = DB_REP_CLIENT;
696			break;
697		case TCL_RPSTRT_MASTER:
698			flag = DB_REP_MASTER;
699			break;
700		}
701	}
702
703	_debug_check();
704	ret = dbenv->rep_start(dbenv, NULL, flag);
705	return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret), "env rep_start"));
706}
707#endif
708
709#ifdef CONFIG_TEST
710/*
711 * tcl_RepProcessMessage --
712 *	Call DB_ENV->rep_process_message().
713 *
714 * PUBLIC: int tcl_RepProcessMessage
715 * PUBLIC:     __P((Tcl_Interp *, int, Tcl_Obj * CONST *, DB_ENV *));
716 */
717int
718tcl_RepProcessMessage(interp, objc, objv, dbenv)
719	Tcl_Interp *interp;		/* Interpreter */
720	int objc;			/* How many arguments? */
721	Tcl_Obj *CONST objv[];		/* The argument objects */
722	DB_ENV *dbenv;			/* Environment pointer */
723{
724	DBT control, rec;
725	DB_LSN permlsn;
726	Tcl_Obj *lsnlist, *myobjv[2], *res;
727	void *ctmp, *rtmp;
728	char *msg;
729	int eid;
730	int freectl, freerec, myobjc, result, ret;
731
732	if (objc != 5) {
733		Tcl_WrongNumArgs(interp, 5, objv, "id control rec");
734		return (TCL_ERROR);
735	}
736	freectl = freerec = 0;
737
738	memset(&control, 0, sizeof(control));
739	memset(&rec, 0, sizeof(rec));
740
741	if ((result = Tcl_GetIntFromObj(interp, objv[2], &eid)) != TCL_OK)
742		return (result);
743
744	ret = _CopyObjBytes(interp, objv[3], &ctmp,
745	    &control.size, &freectl);
746	if (ret != 0) {
747		result = _ReturnSetup(interp, ret,
748		    DB_RETOK_REPPMSG(ret), "rep_proc_msg");
749		return (result);
750	}
751	control.data = ctmp;
752	ret = _CopyObjBytes(interp, objv[4], &rtmp,
753	    &rec.size, &freerec);
754	if (ret != 0) {
755		result = _ReturnSetup(interp, ret,
756		    DB_RETOK_REPPMSG(ret), "rep_proc_msg");
757		goto out;
758	}
759	rec.data = rtmp;
760	_debug_check();
761	ret = dbenv->rep_process_message(dbenv, &control, &rec, eid, &permlsn);
762	/*
763	 * !!!
764	 * The TCL API diverges from the C++/Java APIs here.  For us, it
765	 * is OK to get DUPMASTER and HOLDELECTION for testing purposes.
766	 */
767	result = _ReturnSetup(interp, ret,
768	    DB_RETOK_REPPMSG(ret) || ret == DB_REP_DUPMASTER ||
769	    ret == DB_REP_HOLDELECTION,
770	    "env rep_process_message");
771
772	if (result != TCL_OK)
773		goto out;
774
775	/*
776	 * We have a valid return.  We need to return a variety of information.
777	 * It will be one of the following:
778	 * {0 0} -  Make a 0 return a list for consistent return structure.
779	 * {DUPMASTER 0} -  DUPMASTER, no other info needed.
780	 * {HOLDELECTION 0} -  HOLDELECTION, no other info needed.
781	 * {NEWMASTER #} - NEWMASTER and its ID.
782	 * {NEWSITE 0} - NEWSITE, no other info needed.
783	 * {IGNORE {LSN list}} - IGNORE and this msg's LSN.
784	 * {ISPERM {LSN list}} - ISPERM and the perm LSN.
785	 * {NOTPERM {LSN list}} - NOTPERM and this msg's LSN.
786	 */
787	myobjc = 2;
788	switch (ret) {
789	case 0:
790		myobjv[0] = Tcl_NewIntObj(0);
791		myobjv[1] = Tcl_NewIntObj(0);
792		break;
793	case DB_REP_DUPMASTER:
794		myobjv[0] = Tcl_NewByteArrayObj(
795		    (u_char *)"DUPMASTER", (int)strlen("DUPMASTER"));
796		myobjv[1] = Tcl_NewIntObj(0);
797		break;
798	case DB_REP_HOLDELECTION:
799		myobjv[0] = Tcl_NewByteArrayObj(
800		    (u_char *)"HOLDELECTION", (int)strlen("HOLDELECTION"));
801		myobjv[1] = Tcl_NewIntObj(0);
802		break;
803	case DB_REP_IGNORE:
804		myobjv[0] = Tcl_NewLongObj((long)permlsn.file);
805		myobjv[1] = Tcl_NewLongObj((long)permlsn.offset);
806		lsnlist = Tcl_NewListObj(myobjc, myobjv);
807		myobjv[0] = Tcl_NewByteArrayObj(
808		    (u_char *)"IGNORE", (int)strlen("IGNORE"));
809		myobjv[1] = lsnlist;
810		break;
811	case DB_REP_ISPERM:
812		myobjv[0] = Tcl_NewLongObj((long)permlsn.file);
813		myobjv[1] = Tcl_NewLongObj((long)permlsn.offset);
814		lsnlist = Tcl_NewListObj(myobjc, myobjv);
815		myobjv[0] = Tcl_NewByteArrayObj(
816		    (u_char *)"ISPERM", (int)strlen("ISPERM"));
817		myobjv[1] = lsnlist;
818		break;
819	case DB_REP_NEWSITE:
820		myobjv[0] = Tcl_NewByteArrayObj(
821		    (u_char *)"NEWSITE", (int)strlen("NEWSITE"));
822		myobjv[1] = Tcl_NewIntObj(0);
823		break;
824	case DB_REP_NOTPERM:
825		myobjv[0] = Tcl_NewLongObj((long)permlsn.file);
826		myobjv[1] = Tcl_NewLongObj((long)permlsn.offset);
827		lsnlist = Tcl_NewListObj(myobjc, myobjv);
828		myobjv[0] = Tcl_NewByteArrayObj(
829		    (u_char *)"NOTPERM", (int)strlen("NOTPERM"));
830		myobjv[1] = lsnlist;
831		break;
832	default:
833		msg = db_strerror(ret);
834		Tcl_AppendResult(interp, msg, NULL);
835		Tcl_SetErrorCode(interp, "BerkeleyDB", msg, NULL);
836		result = TCL_ERROR;
837		goto out;
838	}
839	res = Tcl_NewListObj(myobjc, myobjv);
840	if (res != NULL)
841		Tcl_SetObjResult(interp, res);
842out:
843	if (freectl)
844		__os_free(NULL, ctmp);
845	if (freerec)
846		__os_free(NULL, rtmp);
847
848	return (result);
849}
850#endif
851
852#ifdef CONFIG_TEST
853/*
854 * tcl_RepStat --
855 *	Call DB_ENV->rep_stat().
856 *
857 * PUBLIC: int tcl_RepStat
858 * PUBLIC:     __P((Tcl_Interp *, int, Tcl_Obj * CONST *, DB_ENV *));
859 */
860int
861tcl_RepStat(interp, objc, objv, dbenv)
862	Tcl_Interp *interp;		/* Interpreter */
863	int objc;			/* How many arguments? */
864	Tcl_Obj *CONST objv[];		/* The argument objects */
865	DB_ENV *dbenv;
866{
867	DB_REP_STAT *sp;
868	Tcl_Obj *myobjv[2], *res, *thislist, *lsnlist;
869	u_int32_t flag;
870	int myobjc, result, ret;
871	char *arg;
872
873	flag = 0;
874	result = TCL_OK;
875
876	if (objc > 3) {
877		Tcl_WrongNumArgs(interp, 2, objv, NULL);
878		return (TCL_ERROR);
879	}
880	if (objc == 3) {
881		arg = Tcl_GetStringFromObj(objv[2], NULL);
882		if (strcmp(arg, "-clear") == 0)
883			flag = DB_STAT_CLEAR;
884		else {
885			Tcl_SetResult(interp,
886			    "db stat: unknown arg", TCL_STATIC);
887			return (TCL_ERROR);
888		}
889	}
890
891	_debug_check();
892	ret = dbenv->rep_stat(dbenv, &sp, flag);
893	result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
894	    "rep stat");
895	if (result == TCL_ERROR)
896		return (result);
897
898	/*
899	 * Have our stats, now construct the name value
900	 * list pairs and free up the memory.
901	 */
902	res = Tcl_NewObj();
903#ifdef HAVE_STATISTICS
904	/*
905	 * MAKE_STAT_* assumes 'res' and 'error' label.
906	 */
907	if (sp->st_status == DB_REP_MASTER)
908		MAKE_STAT_LIST("Master", 1);
909	else
910		MAKE_STAT_LIST("Client", 1);
911	MAKE_STAT_LSN("Next LSN expected", &sp->st_next_lsn);
912	MAKE_STAT_LSN("First missed LSN", &sp->st_waiting_lsn);
913	MAKE_STAT_LSN("Maximum permanent LSN", &sp->st_max_perm_lsn);
914	MAKE_STAT_LIST("Bulk buffer fills", sp->st_bulk_fills);
915	MAKE_STAT_LIST("Bulk buffer overflows", sp->st_bulk_overflows);
916	MAKE_STAT_LIST("Bulk records stored", sp->st_bulk_records);
917	MAKE_STAT_LIST("Bulk buffer transfers", sp->st_bulk_transfers);
918	MAKE_STAT_LIST("Client service requests", sp->st_client_svc_req);
919	MAKE_STAT_LIST("Client service req misses", sp->st_client_svc_miss);
920	MAKE_STAT_LIST("Client rerequests", sp->st_client_rerequests);
921	MAKE_STAT_LIST("Duplicate master conditions", sp->st_dupmasters);
922	MAKE_STAT_LIST("Environment ID", sp->st_env_id);
923	MAKE_STAT_LIST("Environment priority", sp->st_env_priority);
924	MAKE_STAT_LIST("Generation number", sp->st_gen);
925	MAKE_STAT_LIST("Election generation number", sp->st_egen);
926	MAKE_STAT_LIST("Startup complete", sp->st_startup_complete);
927	MAKE_STAT_LIST("Duplicate log records received", sp->st_log_duplicated);
928	MAKE_STAT_LIST("Current log records queued", sp->st_log_queued);
929	MAKE_STAT_LIST("Maximum log records queued", sp->st_log_queued_max);
930	MAKE_STAT_LIST("Total log records queued", sp->st_log_queued_total);
931	MAKE_STAT_LIST("Log records received", sp->st_log_records);
932	MAKE_STAT_LIST("Log records requested", sp->st_log_requested);
933	MAKE_STAT_LIST("Master environment ID", sp->st_master);
934	MAKE_STAT_LIST("Master changes", sp->st_master_changes);
935	MAKE_STAT_LIST("Messages with bad generation number",
936	    sp->st_msgs_badgen);
937	MAKE_STAT_LIST("Messages processed", sp->st_msgs_processed);
938	MAKE_STAT_LIST("Messages ignored for recovery", sp->st_msgs_recover);
939	MAKE_STAT_LIST("Message send failures", sp->st_msgs_send_failures);
940	MAKE_STAT_LIST("Messages sent", sp->st_msgs_sent);
941	MAKE_STAT_LIST("New site messages", sp->st_newsites);
942	MAKE_STAT_LIST("Number of sites in replication group", sp->st_nsites);
943	MAKE_STAT_LIST("Transmission limited", sp->st_nthrottles);
944	MAKE_STAT_LIST("Outdated conditions", sp->st_outdated);
945	MAKE_STAT_LIST("Transactions applied", sp->st_txns_applied);
946	MAKE_STAT_LIST("Next page expected", sp->st_next_pg);
947	MAKE_STAT_LIST("First missed page", sp->st_waiting_pg);
948	MAKE_STAT_LIST("Duplicate pages received", sp->st_pg_duplicated);
949	MAKE_STAT_LIST("Pages received", sp->st_pg_records);
950	MAKE_STAT_LIST("Pages requested", sp->st_pg_requested);
951	MAKE_STAT_LIST("Elections held", sp->st_elections);
952	MAKE_STAT_LIST("Elections won", sp->st_elections_won);
953	MAKE_STAT_LIST("Election phase", sp->st_election_status);
954	MAKE_STAT_LIST("Election winner", sp->st_election_cur_winner);
955	MAKE_STAT_LIST("Election generation number", sp->st_election_gen);
956	MAKE_STAT_LSN("Election max LSN", &sp->st_election_lsn);
957	MAKE_STAT_LIST("Election sites", sp->st_election_nsites);
958	MAKE_STAT_LIST("Election nvotes", sp->st_election_nvotes);
959	MAKE_STAT_LIST("Election priority", sp->st_election_priority);
960	MAKE_STAT_LIST("Election tiebreaker", sp->st_election_tiebreaker);
961	MAKE_STAT_LIST("Election votes", sp->st_election_votes);
962	MAKE_STAT_LIST("Election seconds", sp->st_election_sec);
963	MAKE_STAT_LIST("Election usecs", sp->st_election_usec);
964	MAKE_STAT_LIST("Start-sync operations delayed",
965	    sp->st_startsync_delayed);
966	MAKE_STAT_LIST("Maximum lease seconds", sp->st_max_lease_sec);
967	MAKE_STAT_LIST("Maximum lease usecs", sp->st_max_lease_usec);
968#endif
969
970	Tcl_SetObjResult(interp, res);
971error:
972	__os_ufree(dbenv->env, sp);
973	return (result);
974}
975
976/*
977 * tcl_RepMgr --
978 *	Configure and start the Replication Manager.
979 *
980 * PUBLIC: int tcl_RepMgr
981 * PUBLIC:     __P((Tcl_Interp *, int, Tcl_Obj * CONST *, DB_ENV *));
982 */
983int
984tcl_RepMgr(interp, objc, objv, dbenv)
985	Tcl_Interp *interp;		/* Interpreter */
986	int objc;			/* How many arguments? */
987	Tcl_Obj *CONST objv[];		/* The argument objects */
988	DB_ENV *dbenv;			/* Environment pointer */
989{
990	static const char *rmgr[] = {
991		"-ack",
992		"-local",
993		"-msgth",
994		"-nsites",
995		"-pri",
996		"-remote",
997		"-start",
998		"-timeout",
999		NULL
1000	};
1001	enum rmgr {
1002		RMGR_ACK,
1003		RMGR_LOCAL,
1004		RMGR_MSGTH,
1005		RMGR_NSITES,
1006		RMGR_PRI,
1007		RMGR_REMOTE,
1008		RMGR_START,
1009		RMGR_TIMEOUT
1010	};
1011	Tcl_Obj **myobjv;
1012	long to;
1013	int ack, i, myobjc, optindex, result, ret, totype;
1014	u_int32_t msgth, remote_flag, start_flag, uintarg;
1015	char *arg;
1016
1017	result = TCL_OK;
1018	ack = ret = totype = 0;
1019	msgth = 1;
1020	remote_flag = start_flag = 0;
1021
1022	if (objc <= 2) {
1023		Tcl_WrongNumArgs(interp, 2, objv, "?args?");
1024		return (TCL_ERROR);
1025	}
1026	/*
1027	 * Get the command name index from the object based on the bdbcmds
1028	 * defined above.
1029	 */
1030	i = 2;
1031	while (i < objc) {
1032		Tcl_ResetResult(interp);
1033		if (Tcl_GetIndexFromObj(interp, objv[i], rmgr, "option",
1034		    TCL_EXACT, &optindex) != TCL_OK) {
1035			result = IS_HELP(objv[i]);
1036			goto error;
1037		}
1038		i++;
1039		switch ((enum rmgr)optindex) {
1040		case RMGR_ACK:
1041			if (i >= objc) {
1042				Tcl_WrongNumArgs(interp, 2, objv,
1043				    "?-ack policy?");
1044				result = TCL_ERROR;
1045				break;
1046			}
1047			arg = Tcl_GetStringFromObj(objv[i++], NULL);
1048			if (strcmp(arg, "all") == 0)
1049				ack = DB_REPMGR_ACKS_ALL;
1050			else if (strcmp(arg, "allpeers") == 0)
1051				ack = DB_REPMGR_ACKS_ALL_PEERS;
1052			else if (strcmp(arg, "none") == 0)
1053				ack = DB_REPMGR_ACKS_NONE;
1054			else if (strcmp(arg, "one") == 0)
1055				ack = DB_REPMGR_ACKS_ONE;
1056			else if (strcmp(arg, "onepeer") == 0)
1057				ack = DB_REPMGR_ACKS_ONE_PEER;
1058			else if (strcmp(arg, "quorum") == 0)
1059				ack = DB_REPMGR_ACKS_QUORUM;
1060			else {
1061				Tcl_AddErrorInfo(interp,
1062				    "ack: illegal policy");
1063				result = TCL_ERROR;
1064				break;
1065			}
1066			_debug_check();
1067			ret = dbenv->repmgr_set_ack_policy(dbenv, ack);
1068			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
1069			    "ack");
1070			break;
1071		case RMGR_LOCAL:
1072			result = Tcl_ListObjGetElements(interp, objv[i],
1073			    &myobjc, &myobjv);
1074			if (result == TCL_OK)
1075				i++;
1076			else
1077				break;
1078			if (myobjc != 2) {
1079				Tcl_WrongNumArgs(interp, 2, objv,
1080				    "?-local {host port}?");
1081				result = TCL_ERROR;
1082				break;
1083			}
1084			arg = Tcl_GetStringFromObj(myobjv[0], NULL);
1085			if ((result = _GetUInt32(interp, myobjv[1], &uintarg))
1086			    != TCL_OK)
1087				break;
1088			_debug_check();
1089			/*
1090			 * No flags for now.
1091			 */
1092			ret = dbenv->repmgr_set_local_site(dbenv,
1093			    arg, uintarg, 0);
1094			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
1095			    "repmgr_set_local_site");
1096			break;
1097		case RMGR_MSGTH:
1098			if (i >= objc) {
1099				Tcl_WrongNumArgs(
1100				    interp, 2, objv, "?-msgth nth?");
1101				result = TCL_ERROR;
1102				break;
1103			}
1104			result = _GetUInt32(interp, objv[i++], &msgth);
1105			break;
1106		case RMGR_NSITES:
1107			if (i >= objc) {
1108				Tcl_WrongNumArgs(interp, 2, objv,
1109				    "?-nsites num_sites?");
1110				result = TCL_ERROR;
1111				break;
1112			}
1113			result = _GetUInt32(interp, objv[i++], &uintarg);
1114			if (result == TCL_OK) {
1115				_debug_check();
1116				ret = dbenv->
1117				    rep_set_nsites(dbenv, uintarg);
1118			}
1119			break;
1120		case RMGR_PRI:
1121			if (i >= objc) {
1122				Tcl_WrongNumArgs(interp, 2, objv,
1123				    "?-pri priority?");
1124				result = TCL_ERROR;
1125				break;
1126			}
1127			result = _GetUInt32(interp, objv[i++], &uintarg);
1128			if (result == TCL_OK) {
1129				_debug_check();
1130				ret = dbenv->
1131				    rep_set_priority(dbenv, uintarg);
1132			}
1133			break;
1134		case RMGR_REMOTE:
1135			result = Tcl_ListObjGetElements(interp, objv[i],
1136			    &myobjc, &myobjv);
1137			if (result == TCL_OK)
1138				i++;
1139			else
1140				break;
1141			if (myobjc != 2 && myobjc != 3) {
1142				Tcl_WrongNumArgs(interp, 2, objv,
1143				    "?-remote {host port [peer]}?");
1144				result = TCL_ERROR;
1145				break;
1146			}
1147			/*
1148			 * Get the flag first so we can reuse 'arg'.
1149			 */
1150			if (myobjc == 3) {
1151				arg = Tcl_GetStringFromObj(myobjv[2], NULL);
1152				if (strcmp(arg, "peer") == 0)
1153					remote_flag = DB_REPMGR_PEER;
1154				else {
1155					Tcl_AddErrorInfo(interp,
1156					    "remote: illegal flag");
1157					result = TCL_ERROR;
1158					break;
1159				}
1160			}
1161			arg = Tcl_GetStringFromObj(myobjv[0], NULL);
1162			if ((result = _GetUInt32(interp, myobjv[1], &uintarg))
1163			    != TCL_OK)
1164				break;
1165			_debug_check();
1166			ret = dbenv->repmgr_add_remote_site(dbenv,
1167			    arg, uintarg, NULL, remote_flag);
1168			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
1169			    "repmgr_add_remote_site");
1170			break;
1171		case RMGR_START:
1172			if (i >= objc) {
1173				Tcl_WrongNumArgs(
1174				    interp, 2, objv, "?-start state?");
1175				result = TCL_ERROR;
1176				break;
1177			}
1178			arg = Tcl_GetStringFromObj(objv[i++], NULL);
1179			if (strcmp(arg, "master") == 0)
1180				start_flag = DB_REP_MASTER;
1181			else if (strcmp(arg, "client") == 0)
1182				start_flag = DB_REP_CLIENT;
1183			else if (strcmp(arg, "elect") == 0)
1184				start_flag = DB_REP_ELECTION;
1185			else {
1186				Tcl_AddErrorInfo(
1187				    interp, "start: illegal state");
1188				result = TCL_ERROR;
1189				break;
1190			}
1191			/*
1192			 * Some config functions need to be called
1193			 * before repmgr_start.  So finish parsing all
1194			 * the args and call repmgr_start at the end.
1195			 */
1196			break;
1197		case RMGR_TIMEOUT:
1198			result = Tcl_ListObjGetElements(interp, objv[i],
1199			    &myobjc, &myobjv);
1200			if (result == TCL_OK)
1201				i++;
1202			else
1203				break;
1204			if (myobjc != 2) {
1205				Tcl_WrongNumArgs(interp, 2, objv,
1206				    "?-timeout {type to}?");
1207				result = TCL_ERROR;
1208				break;
1209			}
1210			arg = Tcl_GetStringFromObj(myobjv[0], NULL);
1211			if (strcmp(arg, "ack") == 0)
1212				totype = DB_REP_ACK_TIMEOUT;
1213			else if (strcmp(arg, "elect") == 0)
1214				totype = DB_REP_ELECTION_TIMEOUT;
1215			else if (strcmp(arg, "elect_retry") == 0)
1216				totype = DB_REP_ELECTION_RETRY;
1217			else if (strcmp(arg, "conn_retry") == 0)
1218				totype = DB_REP_CONNECTION_RETRY;
1219			else {
1220				Tcl_AddErrorInfo(interp,
1221				    "timeout: illegal type");
1222				result = TCL_ERROR;
1223				break;
1224			}
1225			if ((result = Tcl_GetLongFromObj(
1226			   interp, myobjv[1], &to)) != TCL_OK)
1227				break;
1228			_debug_check();
1229			ret = dbenv->rep_set_timeout(dbenv, totype,
1230			    (db_timeout_t)to);
1231			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
1232			    "rep_set_timeout");
1233			break;
1234		}
1235		/*
1236		 * If, at any time, parsing the args we get an error,
1237		 * bail out and return.
1238		 */
1239		if (result != TCL_OK)
1240			goto error;
1241	}
1242	/*
1243	 * Only call repmgr_start if needed.  The user may use this
1244	 * call just to reconfigure, change policy, etc.
1245	 */
1246	if (start_flag != 0 && result == TCL_OK) {
1247		_debug_check();
1248		ret = dbenv->repmgr_start(dbenv, (int)msgth, start_flag);
1249		result = _ReturnSetup(
1250		    interp, ret, DB_RETOK_STD(ret), "repmgr_start");
1251	}
1252error:
1253	return (result);
1254}
1255
1256/*
1257 * tcl_RepMgrStat --
1258 *	Call DB_ENV->repmgr_stat().
1259 *
1260 * PUBLIC: int tcl_RepMgrStat
1261 * PUBLIC:     __P((Tcl_Interp *, int, Tcl_Obj * CONST *, DB_ENV *));
1262 */
1263int
1264tcl_RepMgrStat(interp, objc, objv, dbenv)
1265	Tcl_Interp *interp;		/* Interpreter */
1266	int objc;			/* How many arguments? */
1267	Tcl_Obj *CONST objv[];		/* The argument objects */
1268	DB_ENV *dbenv;
1269{
1270	DB_REPMGR_STAT *sp;
1271	Tcl_Obj *res;
1272	u_int32_t flag;
1273	int result, ret;
1274	char *arg;
1275
1276	flag = 0;
1277	result = TCL_OK;
1278
1279	if (objc > 3) {
1280		Tcl_WrongNumArgs(interp, 2, objv, NULL);
1281		return (TCL_ERROR);
1282	}
1283	if (objc == 3) {
1284		arg = Tcl_GetStringFromObj(objv[2], NULL);
1285		if (strcmp(arg, "-clear") == 0)
1286			flag = DB_STAT_CLEAR;
1287		else {
1288			Tcl_SetResult(interp,
1289			    "db stat: unknown arg", TCL_STATIC);
1290			return (TCL_ERROR);
1291		}
1292	}
1293
1294	_debug_check();
1295	ret = dbenv->repmgr_stat(dbenv, &sp, flag);
1296	result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
1297	    "repmgr stat");
1298	if (result == TCL_ERROR)
1299		return (result);
1300
1301	/*
1302	 * Have our stats, now construct the name value
1303	 * list pairs and free up the memory.
1304	 */
1305	res = Tcl_NewObj();
1306#ifdef HAVE_STATISTICS
1307	/*
1308	 * MAKE_STAT_* assumes 'res' and 'error' label.
1309	 */
1310	MAKE_STAT_LIST("Acknowledgement failures", sp->st_perm_failed);
1311	MAKE_STAT_LIST("Messages delayed", sp->st_msgs_queued);
1312	MAKE_STAT_LIST("Messages discarded", sp->st_msgs_dropped);
1313	MAKE_STAT_LIST("Connections dropped", sp->st_connection_drop);
1314	MAKE_STAT_LIST("Failed re-connects", sp->st_connect_fail);
1315#endif
1316
1317	Tcl_SetObjResult(interp, res);
1318error:
1319	__os_ufree(dbenv->env, sp);
1320	return (result);
1321}
1322#endif
1323