1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1996,2008 Oracle. All rights reserved. 5 * 6 * $Id: client.c,v 12.16 2008/01/08 20:58:49 bostic Exp $ 7 */ 8 9#include "db_config.h" 10 11#include "db_int.h" 12#include "dbinc/db_page.h" 13#include "dbinc/db_am.h" 14#include "dbinc/txn.h" 15 16#ifdef HAVE_SYSTEM_INCLUDE_FILES 17#ifdef HAVE_VXWORKS 18#include <rpcLib.h> 19#else 20#include <rpc/rpc.h> 21#endif 22#endif 23#include "db_server.h" 24#include "dbinc_auto/rpc_client_ext.h" 25 26static int __dbcl_c_destroy __P((DBC *)); 27static int __dbcl_txn_close __P((ENV *)); 28 29/* 30 * __dbcl_env_set_rpc_server -- 31 * Initialize an environment's server. 32 * 33 * PUBLIC: int __dbcl_env_set_rpc_server 34 * PUBLIC: __P((DB_ENV *, void *, const char *, long, long, u_int32_t)); 35 */ 36int 37__dbcl_env_set_rpc_server(dbenv, clnt, host, tsec, ssec, flags) 38 DB_ENV *dbenv; 39 void *clnt; 40 const char *host; 41 long tsec, ssec; 42 u_int32_t flags; 43{ 44 CLIENT *cl; 45 ENV *env; 46 struct timeval tp; 47 48 COMPQUIET(flags, 0); 49 50 env = dbenv->env; 51 52#ifdef HAVE_VXWORKS 53 if (rpcTaskInit() != 0) { 54 __db_errx(env, "Could not initialize VxWorks RPC"); 55 return (ERROR); 56 } 57#endif 58 if (RPC_ON(dbenv)) { 59 __db_errx(env, "Already set an RPC handle"); 60 return (EINVAL); 61 } 62 /* 63 * Only create the client and set its timeout if the user 64 * did not pass us a client structure to begin with. 65 */ 66 if (clnt == NULL) { 67 if ((cl = clnt_create((char *)host, DB_RPC_SERVERPROG, 68 DB_RPC_SERVERVERS, "tcp")) == NULL) { 69 __db_errx(env, clnt_spcreateerror((char *)host)); 70 return (DB_NOSERVER); 71 } 72 if (tsec != 0) { 73 tp.tv_sec = tsec; 74 tp.tv_usec = 0; 75 (void)clnt_control(cl, CLSET_TIMEOUT, (char *)&tp); 76 } 77 } else { 78 cl = (CLIENT *)clnt; 79 F_SET(dbenv, DB_ENV_RPCCLIENT_GIVEN); 80 } 81 dbenv->cl_handle = cl; 82 83 return (__dbcl_env_create(dbenv, ssec)); 84} 85 86/* 87 * __dbcl_env_close_wrap -- 88 * Wrapper function for DB_ENV->close function for clients. 89 * We need a wrapper function to deal with the case where we 90 * either don't call dbenv->open or close gets an error. 91 * We need to release the handle no matter what. 92 * 93 * PUBLIC: int __dbcl_env_close_wrap __P((DB_ENV *, u_int32_t)); 94 */ 95int 96__dbcl_env_close_wrap(dbenv, flags) 97 DB_ENV * dbenv; 98 u_int32_t flags; 99{ 100 int ret, t_ret; 101 102 ret = __dbcl_env_close(dbenv, flags); 103 t_ret = __dbcl_refresh(dbenv); 104 __db_env_destroy(dbenv); 105 if (ret == 0 && t_ret != 0) 106 ret = t_ret; 107 return (ret); 108} 109 110/* 111 * __dbcl_env_open_wrap -- 112 * Wrapper function for DB_ENV->open function for clients. 113 * We need a wrapper function to deal with DB_USE_ENVIRON* flags 114 * and we don't want to complicate the generated code for env_open. 115 * 116 * PUBLIC: int __dbcl_env_open_wrap 117 * PUBLIC: __P((DB_ENV *, const char *, u_int32_t, int)); 118 */ 119int 120__dbcl_env_open_wrap(dbenv, home, flags, mode) 121 DB_ENV * dbenv; 122 const char * home; 123 u_int32_t flags; 124 int mode; 125{ 126 ENV *env; 127 int ret; 128 129 env = dbenv->env; 130 131 if (LF_ISSET(DB_THREAD)) { 132 __db_errx(env, "DB_THREAD not allowed on RPC clients"); 133 return (EINVAL); 134 } 135 136 if ((ret = __env_config(dbenv, home, flags, mode)) != 0) 137 return (ret); 138 139 return (__dbcl_env_open(dbenv, env->db_home, flags, mode)); 140} 141 142/* 143 * __dbcl_db_open_wrap -- 144 * Wrapper function for DB->open function for clients. 145 * We need a wrapper function to error on DB_THREAD flag. 146 * and we don't want to complicate the generated code. 147 * 148 * PUBLIC: int __dbcl_db_open_wrap 149 * PUBLIC: __P((DB *, DB_TXN *, const char *, const char *, 150 * PUBLIC: DBTYPE, u_int32_t, int)); 151 */ 152int 153__dbcl_db_open_wrap(dbp, txnp, name, subdb, type, flags, mode) 154 DB * dbp; 155 DB_TXN * txnp; 156 const char * name; 157 const char * subdb; 158 DBTYPE type; 159 u_int32_t flags; 160 int mode; 161{ 162 return (__dbcl_db_open(dbp, txnp, name, subdb, type, flags, mode)); 163} 164 165/* 166 * __dbcl_refresh -- 167 * Clean up an environment. 168 * 169 * PUBLIC: int __dbcl_refresh __P((DB_ENV *)); 170 */ 171int 172__dbcl_refresh(dbenv) 173 DB_ENV *dbenv; 174{ 175 CLIENT *cl; 176 ENV *env; 177 int ret; 178 char **p; 179 180 cl = (CLIENT *)dbenv->cl_handle; 181 env = dbenv->env; 182 183 ret = 0; 184 if (env->tx_handle != NULL) { 185 /* 186 * We only need to free up our stuff, the caller 187 * of this function will call the server who will 188 * do all the real work. 189 */ 190 ret = __dbcl_txn_close(env); 191 env->tx_handle = NULL; 192 } 193 if (!F_ISSET(dbenv, DB_ENV_RPCCLIENT_GIVEN) && cl != NULL) 194 clnt_destroy(cl); 195 dbenv->cl_handle = NULL; 196 /* 197 * Release any string-based configuration parameters we've copied. 198 * This section is copied from __env_close. 199 */ 200 if (dbenv->db_log_dir != NULL) 201 __os_free(env, dbenv->db_log_dir); 202 dbenv->db_log_dir = NULL; 203 if (dbenv->db_tmp_dir != NULL) 204 __os_free(env, dbenv->db_tmp_dir); 205 dbenv->db_tmp_dir = NULL; 206 if (dbenv->db_data_dir != NULL) { 207 for (p = dbenv->db_data_dir; *p != NULL; ++p) 208 __os_free(env, *p); 209 __os_free(env, dbenv->db_data_dir); 210 dbenv->db_data_dir = NULL; 211 dbenv->data_next = 0; 212 } 213 if (env->db_home != NULL) { 214 __os_free(env, env->db_home); 215 env->db_home = NULL; 216 } 217 return (ret); 218} 219 220/* 221 * __dbcl_retcopy -- 222 * Copy the returned data into the user's DBT, handling allocation flags, 223 * but not DB_DBT_PARTIAL. 224 * 225 * PUBLIC: int __dbcl_retcopy __P((ENV *, DBT *, 226 * PUBLIC: void *, u_int32_t, void **, u_int32_t *)); 227 */ 228int 229__dbcl_retcopy(env, dbt, data, len, memp, memsize) 230 ENV *env; 231 DBT *dbt; 232 void *data; 233 u_int32_t len; 234 void **memp; 235 u_int32_t *memsize; 236{ 237 u_int32_t orig_flags; 238 int ret; 239 240 /* 241 * The RPC server handles DB_DBT_PARTIAL, so we mask it out here to 242 * avoid the handling of partials in __db_retcopy. Check first whether 243 * the data has actually changed, so we don't try to copy over 244 * read-only keys, which the RPC server always returns regardless. 245 */ 246 orig_flags = dbt->flags; 247 F_CLR(dbt, DB_DBT_PARTIAL); 248 if (dbt->data != NULL && dbt->size == len && 249 memcmp(dbt->data, data, len) == 0) 250 ret = 0; 251 else 252 ret = __db_retcopy(env, dbt, data, len, memp, memsize); 253 dbt->flags = orig_flags; 254 return (ret); 255} 256 257/* 258 * __dbcl_txn_close -- 259 * Clean up an environment's transactions. 260 */ 261static int 262__dbcl_txn_close(env) 263 ENV *env; 264{ 265 DB_TXN *txnp; 266 DB_TXNMGR *tmgrp; 267 int ret; 268 269 ret = 0; 270 tmgrp = env->tx_handle; 271 272 /* 273 * This function can only be called once per process (i.e., not 274 * once per thread), so no synchronization is required. 275 * Also this function is called *after* the server has been called, 276 * so the server has already closed/aborted any transactions that 277 * were open on its side. We only need to do local cleanup. 278 */ 279 while ((txnp = TAILQ_FIRST(&tmgrp->txn_chain)) != NULL) 280 __dbcl_txn_end(txnp); 281 282 __os_free(env, tmgrp); 283 return (ret); 284} 285 286/* 287 * __dbcl_txn_end -- 288 * Clean up an transaction. 289 * RECURSIVE FUNCTION: Clean up nested transactions. 290 * 291 * PUBLIC: void __dbcl_txn_end __P((DB_TXN *)); 292 */ 293void 294__dbcl_txn_end(txnp) 295 DB_TXN *txnp; 296{ 297 DB_TXN *kids; 298 DB_TXNMGR *mgr; 299 ENV *env; 300 301 mgr = txnp->mgrp; 302 env = mgr->env; 303 304 /* 305 * First take care of any kids we have 306 */ 307 for (kids = TAILQ_FIRST(&txnp->kids); 308 kids != NULL; 309 kids = TAILQ_FIRST(&txnp->kids)) 310 __dbcl_txn_end(kids); 311 312 /* 313 * We are ending this transaction no matter what the parent 314 * may eventually do, if we have a parent. All those details 315 * are taken care of by the server. We only need to make sure 316 * that we properly release resources. 317 */ 318 if (txnp->parent != NULL) 319 TAILQ_REMOVE(&txnp->parent->kids, txnp, klinks); 320 TAILQ_REMOVE(&mgr->txn_chain, txnp, links); 321 __os_free(env, txnp); 322} 323 324/* 325 * __dbcl_txn_setup -- 326 * Setup a client transaction structure. 327 * 328 * PUBLIC: void __dbcl_txn_setup __P((ENV *, DB_TXN *, DB_TXN *, u_int32_t)); 329 */ 330void 331__dbcl_txn_setup(env, txn, parent, id) 332 ENV *env; 333 DB_TXN *txn; 334 DB_TXN *parent; 335 u_int32_t id; 336{ 337 txn->mgrp = env->tx_handle; 338 txn->parent = parent; 339 txn->txnid = id; 340 341 /* 342 * XXX 343 * In DB library the txn_chain is protected by the mgrp->mutexp. 344 * However, that mutex is implemented in the environments shared 345 * memory region. The client library does not support all of the 346 * region - that just get forwarded to the server. Therefore, 347 * the chain is unprotected here, but properly protected on the 348 * server. 349 */ 350 TAILQ_INSERT_TAIL(&txn->mgrp->txn_chain, txn, links); 351 352 TAILQ_INIT(&txn->kids); 353 354 if (parent != NULL) 355 TAILQ_INSERT_HEAD(&parent->kids, txn, klinks); 356 357 __dbcl_txn_init(txn); 358 359 txn->flags = TXN_MALLOC; 360} 361 362/* 363 * __dbcl_c_destroy -- 364 * Destroy a cursor. 365 */ 366static int 367__dbcl_c_destroy(dbc) 368 DBC *dbc; 369{ 370 DB *dbp; 371 ENV *env; 372 373 dbp = dbc->dbp; 374 env = dbc->env; 375 376 TAILQ_REMOVE(&dbp->free_queue, dbc, links); 377 /* Discard any memory used to store returned data. */ 378 if (dbc->my_rskey.data != NULL) 379 __os_free(env, dbc->my_rskey.data); 380 if (dbc->my_rkey.data != NULL) 381 __os_free(env, dbc->my_rkey.data); 382 if (dbc->my_rdata.data != NULL) 383 __os_free(env, dbc->my_rdata.data); 384 __os_free(NULL, dbc); 385 386 return (0); 387} 388 389/* 390 * __dbcl_c_refresh -- 391 * Refresh a cursor. Move it from the active queue to the free queue. 392 * 393 * PUBLIC: void __dbcl_c_refresh __P((DBC *)); 394 */ 395void 396__dbcl_c_refresh(dbc) 397 DBC *dbc; 398{ 399 DB *dbp; 400 401 dbp = dbc->dbp; 402 dbc->flags = 0; 403 dbc->cl_id = 0; 404 405 /* 406 * If dbp->cursor fails locally, we use a local dbc so that 407 * we can close it. In that case, dbp will be NULL. 408 */ 409 if (dbp != NULL) { 410 TAILQ_REMOVE(&dbp->active_queue, dbc, links); 411 TAILQ_INSERT_TAIL(&dbp->free_queue, dbc, links); 412 } 413} 414 415/* 416 * __dbcl_c_setup -- 417 * Allocate a cursor. 418 * 419 * PUBLIC: int __dbcl_c_setup __P((u_int, DB *, DBC **)); 420 */ 421int 422__dbcl_c_setup(cl_id, dbp, dbcp) 423 u_int cl_id; 424 DB *dbp; 425 DBC **dbcp; 426{ 427 DBC *dbc, tmpdbc; 428 int ret; 429 430 if ((dbc = TAILQ_FIRST(&dbp->free_queue)) != NULL) 431 TAILQ_REMOVE(&dbp->free_queue, dbc, links); 432 else { 433 if ((ret = 434 __os_calloc(dbp->env, 1, sizeof(DBC), &dbc)) != 0) { 435 /* 436 * If we die here, set up a tmp dbc to call the 437 * server to shut down that cursor. 438 */ 439 tmpdbc.dbp = NULL; 440 tmpdbc.cl_id = cl_id; 441 (void)__dbcl_dbc_close(&tmpdbc); 442 return (ret); 443 } 444 445 __dbcl_dbc_init(dbc); 446 447 /* 448 * !!! 449 * Set up the local destroy function -- we're not really 450 * an access method, but it does what we need. 451 */ 452 dbc->am_destroy = __dbcl_c_destroy; 453 } 454 455 dbc->cl_id = cl_id; 456 dbc->dbenv = dbp->dbenv; 457 dbc->env = dbp->env; 458 dbc->dbp = dbp; 459 TAILQ_INSERT_TAIL(&dbp->active_queue, dbc, links); 460 *dbcp = dbc; 461 return (0); 462} 463 464/* 465 * __dbcl_dbclose_common -- 466 * Common code for closing/cleaning a dbp. 467 * 468 * PUBLIC: int __dbcl_dbclose_common __P((DB *)); 469 */ 470int 471__dbcl_dbclose_common(dbp) 472 DB *dbp; 473{ 474 DBC *dbc; 475 ENV *env; 476 int ret, t_ret; 477 478 env = dbp->env; 479 480 /* 481 * Go through the active cursors and call the cursor recycle routine, 482 * which resolves pending operations and moves the cursors onto the 483 * free list. Then, walk the free list and call the cursor destroy 484 * routine. 485 * 486 * NOTE: We do not need to use the join_queue for join cursors. 487 * See comment in __dbcl_dbjoin_ret. 488 */ 489 ret = 0; 490 while ((dbc = TAILQ_FIRST(&dbp->active_queue)) != NULL) 491 __dbcl_c_refresh(dbc); 492 while ((dbc = TAILQ_FIRST(&dbp->free_queue)) != NULL) 493 if ((t_ret = __dbcl_c_destroy(dbc)) != 0 && ret == 0) 494 ret = t_ret; 495 496 TAILQ_INIT(&dbp->free_queue); 497 TAILQ_INIT(&dbp->active_queue); 498 /* Discard any memory used to store returned data. */ 499 if (dbp->my_rskey.data != NULL) 500 __os_free(env, dbp->my_rskey.data); 501 if (dbp->my_rkey.data != NULL) 502 __os_free(env, dbp->my_rkey.data); 503 if (dbp->my_rdata.data != NULL) 504 __os_free(env, dbp->my_rdata.data); 505 506 memset(dbp, CLEAR_BYTE, sizeof(*dbp)); 507 __os_free(NULL, dbp); 508 return (ret); 509} 510