1#include <sys/types.h>
2
3#include <ctype.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7#include <time.h>
8#include <unistd.h>
9
10#include <atmi.h>
11#include <fml1632.h>
12#include <fml32.h>
13#include <tx.h>
14#include <xa.h>
15
16#include <db.h>
17
18#include "datafml.h"
19#include "hdbrec.h"
20#include "hcommonxa.h"
21#include "htimestampxa.h"
22
23/*
24 * The two servers are largely identical, #ifdef the source code.
25 */
26#ifdef SERVER1
27#define	TXN_FUNC		TestTxn1
28#define	TXN_STRING		"TestTxn1"
29#endif
30#ifdef SERVER2
31#define	TXN_FUNC		TestTxn2
32#define	TXN_STRING		"TestTxn2"
33#endif
34void TXN_FUNC(TPSVCINFO *);
35
36#define	HOME	"../data"
37#define	TABLE1	"table1.db"
38#define	TABLE2	"table2.db"
39
40#ifdef VERBOSE
41static int verbose = 1;				/* Debugging output. */
42#else
43static int verbose = 0;
44#endif
45
46DB *db1, *db2;					/* Table handles. */
47
48int cnt_forward;				/* Forwarded requests. */
49int cnt_request;				/* Total requests. */
50
51char *progname;					/* Server run-time name. */
52
53char *db_buf(DBT *);
54
55int
56tpsvrinit(int argc, char* argv[])
57{
58	DB_ENV *dbenv;
59	int ret;
60
61	progname = argv[0];
62	if (verbose)
63		printf("%s: called\n", progname);
64
65	/*
66	 * This is all Berkeley DB specific.
67	 *
68	 * Open the environment and clear tables #1 and #2.  We do this with
69	 * our own DB_ENV handle -- Berkeley DB doesn't require servers know
70	 * where the database environment is, but it's pretty much necessary
71	 * if you want to do anything tricky.
72	 */
73	if ((ret = db_env_create(&dbenv, 0)) != 0) {
74		fprintf(stderr, "db_env_create: %s\n", db_strerror(ret));
75		return (-1);
76	}
77	if ((ret = dbenv->open(dbenv, HOME, DB_JOINENV, 0)) != 0) {
78		fprintf(stderr,
79		    "DB_ENV->open: %s: %s\n", HOME, db_strerror(ret));
80		return (-1);
81	}
82	if ((ret = db_create(&db1, dbenv, 0)) != 0) {
83		fprintf(stderr, "db_create: %s\n", db_strerror(ret));
84		return (-1);
85	}
86	db1->set_errfile(db1, stderr);
87	if ((ret = db1->open(db1, NULL,
88	    TABLE1, NULL, DB_BTREE, DB_AUTO_COMMIT | DB_CREATE, 0660)) != 0 ||
89	    (ret = db1->truncate(db1, NULL, NULL, 0)) != 0) {
90		fprintf(stderr,
91		    "DB open/truncate: %s: %s\n", TABLE1, db_strerror(ret));
92		return (-1);
93	}
94	if ((ret = db_create(&db2, dbenv, 0)) != 0) {
95		fprintf(stderr, "db_create: %s\n", db_strerror(ret));
96		return (-1);
97	}
98	db2->set_errfile(db2, stderr);
99	if ((ret = db2->open(db2, NULL,
100	    TABLE2, NULL, DB_BTREE, DB_AUTO_COMMIT | DB_CREATE, 0660)) != 0 ||
101	    (ret = db2->truncate(db2, NULL, NULL, 0)) != 0) {
102		fprintf(stderr,
103		    "DB open/truncate: %s: %s\n", TABLE2, db_strerror(ret));
104		return (-1);
105	}
106
107	/* We're done -- discard our handles. */
108	if ((ret = db1->close(db1, 0)) != 0) {
109		fprintf(stderr,
110		    "DB->close: %s: %s\n", TABLE1, db_strerror(ret));
111		return (-1);
112	}
113	if ((ret = db2->close(db2, 0)) != 0) {
114		fprintf(stderr,
115		    "DB->close: %s: %s\n", TABLE2, db_strerror(ret));
116		return (-1);
117	}
118	if ((ret = dbenv->close(dbenv, 0)) != 0) {
119		fprintf(stderr,
120		    "DB_ENV->close: %s: %s\n", HOME, db_strerror(ret));
121		return (-1);
122	}
123	/* End Berkeley DB specific setup. */
124
125	/* Open resource managers. */
126	if (tx_open() == TX_ERROR) {
127		fprintf(stderr, "tx_open: TX_ERROR\n");
128		return (-1);
129	}
130
131	/* Seed random number generator. */
132	srand((u_int)(time(NULL) | getpid()));
133
134	/* Open permanent XA handles. */
135	if ((ret = db_create(&db1, NULL, DB_XA_CREATE)) != 0) {
136		fprintf(stderr, "db_create: %s\n", db_strerror(ret));
137		return (-1);
138	}
139	db1->set_errfile(db1, stderr);
140	if ((ret = db1->open(db1, NULL,
141	    TABLE1, NULL, DB_BTREE, DB_AUTO_COMMIT | DB_CREATE, 0660)) != 0) {
142		fprintf(stderr, "DB open: %s: %s\n", TABLE1, db_strerror(ret));
143		return (-1);
144	}
145	if ((ret = db_create(&db2, NULL, DB_XA_CREATE)) != 0) {
146		fprintf(stderr, "db_create: %s\n", db_strerror(ret));
147		return (-1);
148	}
149	db2->set_errfile(db2, stderr);
150	if ((ret = db2->open(db2, NULL,
151	    TABLE2, NULL, DB_BTREE, DB_AUTO_COMMIT | DB_CREATE, 0660)) != 0) {
152		fprintf(stderr, "DB open: %s: %s\n", TABLE2, db_strerror(ret));
153		return (-1);
154	}
155
156	if (verbose)
157		printf("%s: tpsvrinit: initialization done\n", progname);
158
159	return (0);
160}
161
162void
163tpsvrdone()
164{
165	if (db1 != NULL)
166		(void)db1->close(db1, 0);
167	if (db2 != NULL)
168		(void)db2->close(db2, 0);
169
170	tx_close();
171
172	if (verbose)
173		printf("%s: tpsvrdone: shutdown done\n", progname);
174
175	printf("%s: %d requests, %d requests forwarded to the other server\n",
176	    progname, cnt_request, cnt_forward);
177}
178
179void
180TXN_FUNC(TPSVCINFO *msg)
181{
182	DBT data;
183	DBT key;
184	FBFR *replyBuf;
185	HDbRec rcrd;
186	long replyLen, seqNo;
187	int ret;
188
189	++cnt_request;
190
191#ifdef SERVER1
192	/*
193	 * Test that servers can forward to other servers.  Randomly forward
194	 * half of server #1's requests to server #2.
195	 */
196	if (rand() % 2 > 0) {
197		++cnt_forward;
198
199		replyLen = 1024;
200		if ((replyBuf =
201		    (FBFR*)tpalloc("FML32", NULL, replyLen)) == NULL ||
202		    tpcall("TestTxn2", msg->data,
203		    0, (char**)&replyBuf, &replyLen, TPSIGRSTRT) == -1) {
204			fprintf(stderr, "%s: TUXEDO ERROR: %s (code %d)\n",
205			    progname, tpstrerror(tperrno), tperrno);
206			tpfree((char*)replyBuf);
207			tpreturn(TPFAIL, 0L, 0, 0L, 0);
208		} else {
209			tpfree((char*)replyBuf);
210			tpreturn(TPSUCCESS, tpurcode, 0, 0L, 0);
211		}
212		return;
213	}
214#endif
215						/* Read the record. */
216	if (Fget((FBFR*)msg->data, SEQ_NO, 0, (char *)&rcrd.SeqNo, 0) == -1)
217		goto fml_err;
218	if (Fget((FBFR*)msg->data, TS_SEC, 0, (char *)&rcrd.Ts.Sec, 0) == -1)
219		goto fml_err;
220	if (Fget(
221	    (FBFR*)msg->data, TS_USEC, 0, (char *)&rcrd.Ts.Usec, 0) == -1) {
222fml_err:	fprintf(stderr, "%s: FML ERROR: %s (code %d)\n",
223		    progname, Fstrerror(Ferror), Ferror);
224		goto err;
225	}
226
227	seqNo = rcrd.SeqNo;			/* Update the record. */
228	memset(&key, 0, sizeof(key));
229	key.data = &seqNo;
230	key.size = sizeof(seqNo);
231	memset(&data, 0, sizeof(data));
232	data.data = &rcrd;
233	data.size = sizeof(rcrd);
234
235	strcpy(rcrd.Msg, "Table1");		/* Table 1. */
236	if (verbose) {
237		printf("put1: key: %s\n", db_buf(&key));
238		printf("put1: data: %s\n", db_buf(&data));
239	}
240	if ((ret = db1->put(db1, NULL, &key, &data, 0)) != 0) {
241		fprintf(stderr, "%s: %s: Table1->put: %s\n",
242		    progname, TXN_STRING, db_strerror(ret));
243		goto err;
244	}
245
246	strcpy(rcrd.Msg, "Table2");		/* Table 2. */
247	if ((ret = db2->put(db2, NULL, &key, &data, 0)) != 0) {
248		fprintf(stderr, "%s: %s: Table2->put: %s\n",
249		    progname, TXN_STRING, db_strerror(ret));
250		goto err;
251	}
252
253	/*
254	 * Decide if the client is going to commit the global transaction or
255	 * not, testing the return-value path back to the client; this is the
256	 * path we'd use to resolve deadlock, for example.  Commit 80% of the
257	 * time.  Returning 0 causes the client to commit, 1 to abort.
258	 */
259	if (rand() % 10 > 7) {
260		if (verbose)
261			printf("%s: %s: commit\n", progname, TXN_STRING);
262		tpreturn(TPSUCCESS, 0L, 0, 0L, 0);
263	} else {
264		if (verbose)
265			printf("%s: %s: abort\n", progname, TXN_STRING);
266		tpreturn(TPSUCCESS, 1L, 0, 0L, 0);
267	}
268	return;
269
270err:	tpreturn(TPFAIL, 1L, 0, 0L, 0);
271}
272
273char *
274db_buf(dbt)
275	DBT *dbt;
276{
277	static u_char buf[1024];
278	size_t len;
279	u_char *p, *b;
280
281	for (p = dbt->data, len = dbt->size, b = buf; len > 0; ++p, --len)
282		if (isprint(*p))
283			b += sprintf((char *)b, "%c", *p);
284		else
285			b += sprintf((char *)b, "%#o", *p);
286	return ((char *)buf);
287}
288