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