diff options
author | jbj <devnull@localhost> | 2002-08-14 16:31:49 +0000 |
---|---|---|
committer | jbj <devnull@localhost> | 2002-08-14 16:31:49 +0000 |
commit | d481ba55c02407124c499c7800ea556786137bc5 (patch) | |
tree | e0d3fdb7906ae3290f019999e0661cfc5b1b3f58 /db/rpc_server/cxx | |
parent | 9114d6ffea4ee330874ebc8febe225ce0e891eac (diff) | |
download | librpm-tizen-d481ba55c02407124c499c7800ea556786137bc5.tar.gz librpm-tizen-d481ba55c02407124c499c7800ea556786137bc5.tar.bz2 librpm-tizen-d481ba55c02407124c499c7800ea556786137bc5.zip |
Initial revision
CVS patchset: 5630
CVS date: 2002/08/14 16:31:49
Diffstat (limited to 'db/rpc_server/cxx')
-rw-r--r-- | db/rpc_server/cxx/db_server_cxxproc.cpp | 2200 | ||||
-rw-r--r-- | db/rpc_server/cxx/db_server_cxxutil.cpp | 746 |
2 files changed, 2946 insertions, 0 deletions
diff --git a/db/rpc_server/cxx/db_server_cxxproc.cpp b/db/rpc_server/cxx/db_server_cxxproc.cpp new file mode 100644 index 000000000..0ba1fd50c --- /dev/null +++ b/db/rpc_server/cxx/db_server_cxxproc.cpp @@ -0,0 +1,2200 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2001-2002 + * Sleepycat Software. All rights reserved. + */ + +#include "db_config.h" + +#ifdef HAVE_RPC +#ifndef lint +static const char revid[] = "Id: db_server_cxxproc.cpp,v 1.12 2002/08/09 01:56:08 bostic Exp "; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <rpc/rpc.h> + +#include <string.h> +#endif +#include "dbinc_auto/db_server.h" + +#include "db_int.h" +#include "db_cxx.h" + +extern "C" { +#include "dbinc/db_server_int.h" +#include "dbinc_auto/rpc_server_ext.h" +} + +/* BEGIN __env_cachesize_proc */ +extern "C" void +__env_cachesize_proc( + long dbenvcl_id, + u_int32_t gbytes, + u_int32_t bytes, + u_int32_t ncache, + __env_cachesize_reply *replyp) +/* END __env_cachesize_proc */ +{ + DbEnv *dbenv; + ct_entry *dbenv_ctp; + int ret; + + ACTIVATE_CTP(dbenv_ctp, dbenvcl_id, CT_ENV); + dbenv = (DbEnv *)dbenv_ctp->ct_anyp; + + ret = dbenv->set_cachesize(gbytes, bytes, ncache); + + replyp->status = ret; + return; +} + +/* BEGIN __env_close_proc */ +extern "C" void +__env_close_proc( + long dbenvcl_id, + u_int32_t flags, + __env_close_reply *replyp) +/* END __env_close_proc */ +{ + ct_entry *dbenv_ctp; + + ACTIVATE_CTP(dbenv_ctp, dbenvcl_id, CT_ENV); + replyp->status = __dbenv_close_int(dbenvcl_id, flags, 0); + return; +} + +/* BEGIN __env_create_proc */ +extern "C" void +__env_create_proc( + u_int32_t timeout, + __env_create_reply *replyp) +/* END __env_create_proc */ +{ + DbEnv *dbenv; + ct_entry *ctp; + + ctp = new_ct_ent(&replyp->status); + if (ctp == NULL) + return; + + dbenv = new DbEnv(DB_CXX_NO_EXCEPTIONS); + ctp->ct_envp = dbenv; + ctp->ct_type = CT_ENV; + ctp->ct_parent = NULL; + ctp->ct_envparent = ctp; + __dbsrv_settimeout(ctp, timeout); + __dbsrv_active(ctp); + replyp->envcl_id = ctp->ct_id; + + replyp->status = 0; + return; +} + +/* BEGIN __env_dbremove_proc */ +extern "C" void +__env_dbremove_proc( + long dbenvcl_id, + long txnpcl_id, + char *name, + char *subdb, + u_int32_t flags, + __env_dbremove_reply *replyp) +/* END __env_dbremove_proc */ +{ + int ret; + DbEnv *dbenv; + DbTxn *txnp; + ct_entry *dbenv_ctp, *txnp_ctp; + + ACTIVATE_CTP(dbenv_ctp, dbenvcl_id, CT_ENV); + dbenv = (DbEnv *)dbenv_ctp->ct_anyp; + + if (txnpcl_id != 0) { + ACTIVATE_CTP(txnp_ctp, txnpcl_id, CT_TXN); + txnp = (DbTxn *)txnp_ctp->ct_anyp; + } else + txnp = NULL; + + ret = dbenv->dbremove(txnp, name, subdb, flags); + + replyp->status = ret; + return; +} + +/* BEGIN __env_dbrename_proc */ +void +__env_dbrename_proc( + long dbenvcl_id, + long txnpcl_id, + char *name, + char *subdb, + char *newname, + u_int32_t flags, + __env_dbrename_reply *replyp) +/* END __env_dbrename_proc */ +{ + int ret; + DbEnv *dbenv; + DbTxn *txnp; + ct_entry *dbenv_ctp, *txnp_ctp; + + ACTIVATE_CTP(dbenv_ctp, dbenvcl_id, CT_ENV); + dbenv = (DbEnv *)dbenv_ctp->ct_anyp; + + if (txnpcl_id != 0) { + ACTIVATE_CTP(txnp_ctp, txnpcl_id, CT_TXN); + txnp = (DbTxn *)txnp_ctp->ct_anyp; + } else + txnp = NULL; + + ret = dbenv->dbrename(txnp, name, subdb, newname, flags); + + replyp->status = ret; + return; +} + +/* BEGIN __env_encrypt_proc */ +extern "C" void +__env_encrypt_proc( + long dbenvcl_id, + char *passwd, + u_int32_t flags, + __env_encrypt_reply *replyp) +/* END __env_encrypt_proc */ +{ + DbEnv *dbenv; + ct_entry *dbenv_ctp; + int ret; + + ACTIVATE_CTP(dbenv_ctp, dbenvcl_id, CT_ENV); + dbenv = (DbEnv *)dbenv_ctp->ct_anyp; + + ret = dbenv->set_encrypt(passwd, flags); + + replyp->status = ret; + return; +} + +/* BEGIN __env_flags_proc */ +extern "C" void +__env_flags_proc( + long dbenvcl_id, + u_int32_t flags, + u_int32_t onoff, + __env_flags_reply *replyp) +/* END __env_flags_proc */ +{ + DbEnv *dbenv; + ct_entry *dbenv_ctp; + int ret; + + ACTIVATE_CTP(dbenv_ctp, dbenvcl_id, CT_ENV); + dbenv = (DbEnv *)dbenv_ctp->ct_anyp; + + ret = dbenv->set_flags(flags, onoff); + if (onoff) + dbenv_ctp->ct_envdp.onflags = flags; + else + dbenv_ctp->ct_envdp.offflags = flags; + + replyp->status = ret; + return; +} +/* BEGIN __env_open_proc */ +extern "C" void +__env_open_proc( + long dbenvcl_id, + char *home, + u_int32_t flags, + u_int32_t mode, + __env_open_reply *replyp) +/* END __env_open_proc */ +{ + DbEnv *dbenv; + ct_entry *dbenv_ctp, *new_ctp; + u_int32_t newflags, shareflags; + int ret; + home_entry *fullhome; + + ACTIVATE_CTP(dbenv_ctp, dbenvcl_id, CT_ENV); + dbenv = (DbEnv *)dbenv_ctp->ct_anyp; + fullhome = get_home(home); + if (fullhome == NULL) { + ret = DB_NOSERVER_HOME; + goto out; + } + + /* + * If they are using locking do deadlock detection for them, + * internally. + */ + if ((flags & DB_INIT_LOCK) && + (ret = dbenv->set_lk_detect(DB_LOCK_DEFAULT)) != 0) + goto out; + + if (__dbsrv_verbose) { + dbenv->set_errfile(stderr); + dbenv->set_errpfx(fullhome->home); + } + + /* + * Mask off flags we ignore + */ + newflags = (flags & ~DB_SERVER_FLAGMASK); + shareflags = (newflags & DB_SERVER_ENVFLAGS); + /* + * Check now whether we can share a handle for this env. + */ + replyp->envcl_id = dbenvcl_id; + if ((new_ctp = __dbsrv_shareenv(dbenv_ctp, fullhome, shareflags)) + != NULL) { + /* + * We can share, clean up old ID, set new one. + */ + if (__dbsrv_verbose) + printf("Sharing env ID %ld\n", new_ctp->ct_id); + replyp->envcl_id = new_ctp->ct_id; + ret = __dbenv_close_int(dbenvcl_id, 0, 0); + } else { + ret = dbenv->open(fullhome->home, newflags, mode); + dbenv_ctp->ct_envdp.home = fullhome; + dbenv_ctp->ct_envdp.envflags = shareflags; + } +out: replyp->status = ret; + return; +} + +/* BEGIN __env_remove_proc */ +extern "C" void +__env_remove_proc( + long dbenvcl_id, + char *home, + u_int32_t flags, + __env_remove_reply *replyp) +/* END __env_remove_proc */ +{ + DbEnv *dbenv; + ct_entry *dbenv_ctp; + int ret; + home_entry *fullhome; + + ACTIVATE_CTP(dbenv_ctp, dbenvcl_id, CT_ENV); + dbenv = (DbEnv *)dbenv_ctp->ct_anyp; + fullhome = get_home(home); + if (fullhome == NULL) { + replyp->status = DB_NOSERVER_HOME; + return; + } + + ret = dbenv->remove(fullhome->home, flags); + __dbdel_ctp(dbenv_ctp); + replyp->status = ret; + return; +} + +/* BEGIN __txn_abort_proc */ +extern "C" void +__txn_abort_proc( + long txnpcl_id, + __txn_abort_reply *replyp) +/* END __txn_abort_proc */ +{ + DbTxn *txnp; + ct_entry *txnp_ctp; + int ret; + + ACTIVATE_CTP(txnp_ctp, txnpcl_id, CT_TXN); + txnp = (DbTxn *)txnp_ctp->ct_anyp; + + ret = txnp->abort(); + __dbdel_ctp(txnp_ctp); + replyp->status = ret; + return; +} + +/* BEGIN __txn_begin_proc */ +extern "C" void +__txn_begin_proc( + long dbenvcl_id, + long parentcl_id, + u_int32_t flags, + __txn_begin_reply *replyp) +/* END __txn_begin_proc */ +{ + DbEnv *dbenv; + DbTxn *parent, *txnp; + ct_entry *ctp, *dbenv_ctp, *parent_ctp; + int ret; + + ACTIVATE_CTP(dbenv_ctp, dbenvcl_id, CT_ENV); + dbenv = (DbEnv *)dbenv_ctp->ct_anyp; + parent_ctp = NULL; + + ctp = new_ct_ent(&replyp->status); + if (ctp == NULL) + return; + + if (parentcl_id != 0) { + ACTIVATE_CTP(parent_ctp, parentcl_id, CT_TXN); + parent = (DbTxn *)parent_ctp->ct_anyp; + ctp->ct_activep = parent_ctp->ct_activep; + } else + parent = NULL; + + ret = dbenv->txn_begin(parent, &txnp, flags); + if (ret == 0) { + ctp->ct_txnp = txnp; + ctp->ct_type = CT_TXN; + ctp->ct_parent = parent_ctp; + ctp->ct_envparent = dbenv_ctp; + replyp->txnidcl_id = ctp->ct_id; + __dbsrv_settimeout(ctp, dbenv_ctp->ct_timeout); + __dbsrv_active(ctp); + } else + __dbclear_ctp(ctp); + + replyp->status = ret; + return; +} + +/* BEGIN __txn_commit_proc */ +extern "C" void +__txn_commit_proc( + long txnpcl_id, + u_int32_t flags, + __txn_commit_reply *replyp) +/* END __txn_commit_proc */ +{ + DbTxn *txnp; + ct_entry *txnp_ctp; + int ret; + + ACTIVATE_CTP(txnp_ctp, txnpcl_id, CT_TXN); + txnp = (DbTxn *)txnp_ctp->ct_anyp; + + ret = txnp->commit(flags); + __dbdel_ctp(txnp_ctp); + + replyp->status = ret; + return; +} + +/* BEGIN __txn_discard_proc */ +extern "C" void +__txn_discard_proc( + long txnpcl_id, + u_int32_t flags, + __txn_discard_reply *replyp) +/* END __txn_discard_proc */ +{ + DbTxn *txnp; + ct_entry *txnp_ctp; + int ret; + + ACTIVATE_CTP(txnp_ctp, txnpcl_id, CT_TXN); + txnp = (DbTxn *)txnp_ctp->ct_anyp; + + ret = txnp->discard(flags); + __dbdel_ctp(txnp_ctp); + + replyp->status = ret; + return; +} + +/* BEGIN __txn_prepare_proc */ +extern "C" void +__txn_prepare_proc( + long txnpcl_id, + u_int8_t *gid, + __txn_prepare_reply *replyp) +/* END __txn_prepare_proc */ +{ + DbTxn *txnp; + ct_entry *txnp_ctp; + int ret; + + ACTIVATE_CTP(txnp_ctp, txnpcl_id, CT_TXN); + txnp = (DbTxn *)txnp_ctp->ct_anyp; + + ret = txnp->prepare(gid); + replyp->status = ret; + return; +} + +/* BEGIN __txn_recover_proc */ +extern "C" void +__txn_recover_proc( + long dbenvcl_id, + u_int32_t count, + u_int32_t flags, + __txn_recover_reply *replyp, + int * freep) +/* END __txn_recover_proc */ +{ + DbEnv *dbenv; + DbPreplist *dbprep, *p; + ct_entry *dbenv_ctp, *ctp; + long erri, i, retcount; + u_int32_t *txnidp; + int ret; + char *gid; + + ACTIVATE_CTP(dbenv_ctp, dbenvcl_id, CT_ENV); + dbenv = (DbEnv *)dbenv_ctp->ct_anyp; + *freep = 0; + + if ((ret = + __os_malloc(dbenv->get_DB_ENV(), count * sizeof(DbPreplist), &dbprep)) != 0) + goto out; + if ((ret = + dbenv->txn_recover(dbprep, count, &retcount, flags)) != 0) + goto out; + /* + * If there is nothing, success, but it's easy. + */ + replyp->retcount = retcount; // TODO: fix C++ txn_recover + if (retcount == 0) { + replyp->txn.txn_val = NULL; + replyp->txn.txn_len = 0; + replyp->gid.gid_val = NULL; + replyp->gid.gid_len = 0; + } + + /* + * We have our txn list. Now we need to allocate the space for + * the txn ID array and the GID array and set them up. + */ + if ((ret = __os_calloc(dbenv->get_DB_ENV(), retcount, sizeof(u_int32_t), + &replyp->txn.txn_val)) != 0) + goto out; + replyp->txn.txn_len = retcount * sizeof(u_int32_t); + if ((ret = __os_calloc(dbenv->get_DB_ENV(), retcount, DB_XIDDATASIZE, + &replyp->gid.gid_val)) != 0) { + __os_free(dbenv->get_DB_ENV(), replyp->txn.txn_val); + goto out; + } + replyp->gid.gid_len = retcount * DB_XIDDATASIZE; + + /* + * Now walk through our results, creating parallel arrays + * to send back. For each entry we need to create a new + * txn ctp and then fill in the array info. + */ + i = 0; + p = dbprep; + gid = replyp->gid.gid_val; + txnidp = replyp->txn.txn_val; + while (i++ < retcount) { + ctp = new_ct_ent(&ret); + if (ret != 0) { + i--; + goto out2; + } + ctp->ct_txnp = p->txn; + ctp->ct_type = CT_TXN; + ctp->ct_parent = NULL; + ctp->ct_envparent = dbenv_ctp; + __dbsrv_settimeout(ctp, dbenv_ctp->ct_timeout); + __dbsrv_active(ctp); + + *txnidp = ctp->ct_id; + memcpy(gid, p->gid, DB_XIDDATASIZE); + + p++; + txnidp++; + gid += DB_XIDDATASIZE; + } + /* + * If we get here, we have success and we have to set freep + * so it'll get properly freed next time. + */ + *freep = 1; +out: + if (dbprep != NULL) + __os_free(dbenv->get_DB_ENV(), dbprep); + replyp->status = ret; + return; +out2: + /* + * We had an error in the middle of creating our new txn + * ct entries. We have to unwind all that we have done. Ugh. + */ + for (txnidp = replyp->txn.txn_val, erri = 0; + erri < i; erri++, txnidp++) { + ctp = get_tableent(*txnidp); + __dbclear_ctp(ctp); + } + __os_free(dbenv->get_DB_ENV(), replyp->txn.txn_val); + __os_free(dbenv->get_DB_ENV(), replyp->gid.gid_val); + __os_free(dbenv->get_DB_ENV(), dbprep); + replyp->status = ret; + return; +} + +/* BEGIN __db_bt_maxkey_proc */ +extern "C" void +__db_bt_maxkey_proc( + long dbpcl_id, + u_int32_t maxkey, + __db_bt_maxkey_reply *replyp) +/* END __db_bt_maxkey_proc */ +{ + Db *dbp; + ct_entry *dbp_ctp; + int ret; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + + ret = dbp->set_bt_maxkey(maxkey); + + replyp->status = ret; + return; +} + +/* BEGIN __db_associate_proc */ +extern "C" void +__db_associate_proc( + long dbpcl_id, + long txnpcl_id, + long sdbpcl_id, + u_int32_t flags, + __db_associate_reply *replyp) +/* END __db_associate_proc */ +{ + Db *dbp, *sdbp; + DbTxn *txnp; + ct_entry *dbp_ctp, *sdbp_ctp, *txnp_ctp; + int ret; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + ACTIVATE_CTP(sdbp_ctp, sdbpcl_id, CT_DB); + sdbp = (Db *)sdbp_ctp->ct_anyp; + if (txnpcl_id != 0) { + ACTIVATE_CTP(txnp_ctp, txnpcl_id, CT_TXN); + txnp = (DbTxn *)txnp_ctp->ct_anyp; + } else + txnp = NULL; + + /* + * We do not support DB_CREATE for associate. Users + * can only access secondary indices on a read-only basis, + * so whatever they are looking for needs to be there already. + */ + if (flags != 0) + ret = EINVAL; + else + ret = dbp->associate(txnp, sdbp, NULL, flags); + + replyp->status = ret; + return; +} + +/* BEGIN __db_bt_minkey_proc */ +extern "C" void +__db_bt_minkey_proc( + long dbpcl_id, + u_int32_t minkey, + __db_bt_minkey_reply *replyp) +/* END __db_bt_minkey_proc */ +{ + Db *dbp; + ct_entry *dbp_ctp; + int ret; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + + ret = dbp->set_bt_minkey(minkey); + + replyp->status = ret; + return; +} + +/* BEGIN __db_close_proc */ +extern "C" void +__db_close_proc( + long dbpcl_id, + u_int32_t flags, + __db_close_reply *replyp) +/* END __db_close_proc */ +{ + ct_entry *dbp_ctp; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + replyp->status = __db_close_int(dbpcl_id, flags); + return; +} + +/* BEGIN __db_create_proc */ +extern "C" void +__db_create_proc( + long dbenvcl_id, + u_int32_t flags, + __db_create_reply *replyp) +/* END __db_create_proc */ +{ + Db *dbp; + DbEnv *dbenv; + ct_entry *dbenv_ctp, *dbp_ctp; + + ACTIVATE_CTP(dbenv_ctp, dbenvcl_id, CT_ENV); + dbenv = (DbEnv *)dbenv_ctp->ct_anyp; + + dbp_ctp = new_ct_ent(&replyp->status); + if (dbp_ctp == NULL) + return ; + /* + * We actually require env's for databases. The client should + * have caught it, but just in case. + */ + DB_ASSERT(dbenv != NULL); + dbp = new Db(dbenv, flags); + dbp_ctp->ct_dbp = dbp; + dbp_ctp->ct_type = CT_DB; + dbp_ctp->ct_parent = dbenv_ctp; + dbp_ctp->ct_envparent = dbenv_ctp; + replyp->dbcl_id = dbp_ctp->ct_id; + replyp->status = 0; + return; +} + +/* BEGIN __db_del_proc */ +extern "C" void +__db_del_proc( + long dbpcl_id, + long txnpcl_id, + u_int32_t keydlen, + u_int32_t keydoff, + u_int32_t keyulen, + u_int32_t keyflags, + void *keydata, + u_int32_t keysize, + u_int32_t flags, + __db_del_reply *replyp) +/* END __db_del_proc */ +{ + Db *dbp; + DbTxn *txnp; + ct_entry *dbp_ctp, *txnp_ctp; + int ret; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + if (txnpcl_id != 0) { + ACTIVATE_CTP(txnp_ctp, txnpcl_id, CT_TXN); + txnp = (DbTxn *)txnp_ctp->ct_anyp; + } else + txnp = NULL; + + /* Set up key */ + Dbt key(keydata, keysize); + key.set_dlen(keydlen); + key.set_ulen(keyulen); + key.set_doff(keydoff); + key.set_flags(keyflags); + + ret = dbp->del(txnp, &key, flags); + + replyp->status = ret; + return; +} + +/* BEGIN __db_encrypt_proc */ +extern "C" void +__db_encrypt_proc( + long dbpcl_id, + char *passwd, + u_int32_t flags, + __db_encrypt_reply *replyp) +/* END __db_encrypt_proc */ +{ + Db *dbp; + ct_entry *dbp_ctp; + int ret; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + + ret = dbp->set_encrypt(passwd, flags); + replyp->status = ret; + return; +} + +/* BEGIN __db_extentsize_proc */ +extern "C" void +__db_extentsize_proc( + long dbpcl_id, + u_int32_t extentsize, + __db_extentsize_reply *replyp) +/* END __db_extentsize_proc */ +{ + Db *dbp; + ct_entry *dbp_ctp; + int ret; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + + ret = dbp->set_q_extentsize(extentsize); + + replyp->status = ret; + return; +} + +/* BEGIN __db_flags_proc */ +extern "C" void +__db_flags_proc( + long dbpcl_id, + u_int32_t flags, + __db_flags_reply *replyp) +/* END __db_flags_proc */ +{ + Db *dbp; + ct_entry *dbp_ctp; + int ret; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + + ret = dbp->set_flags(flags); + dbp_ctp->ct_dbdp.setflags = flags; + + replyp->status = ret; + return; +} + +/* BEGIN __db_get_proc */ +extern "C" void +__db_get_proc( + long dbpcl_id, + long txnpcl_id, + u_int32_t keydlen, + u_int32_t keydoff, + u_int32_t keyulen, + u_int32_t keyflags, + void *keydata, + u_int32_t keysize, + u_int32_t datadlen, + u_int32_t datadoff, + u_int32_t dataulen, + u_int32_t dataflags, + void *datadata, + u_int32_t datasize, + u_int32_t flags, + __db_get_reply *replyp, + int * freep) +/* END __db_get_proc */ +{ + Db *dbp; + DbTxn *txnp; + ct_entry *dbp_ctp, *txnp_ctp; + int key_alloc, bulk_alloc, ret; + void *tmpdata; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + if (txnpcl_id != 0) { + ACTIVATE_CTP(txnp_ctp, txnpcl_id, CT_TXN); + txnp = (DbTxn *)txnp_ctp->ct_anyp; + } else + txnp = NULL; + + *freep = 0; + bulk_alloc = 0; + + /* Set up key and data */ + Dbt key(keydata, keysize); + key.set_dlen(keydlen); + key.set_ulen(keyulen); + key.set_doff(keydoff); + /* + * Ignore memory related flags on server. + */ + key.set_flags(DB_DBT_MALLOC | (keyflags & DB_DBT_PARTIAL)); + + Dbt data(datadata, datasize); + data.set_dlen(datadlen); + data.set_ulen(dataulen); + data.set_doff(datadoff); + /* + * Ignore memory related flags on server. + */ + dataflags &= DB_DBT_PARTIAL; + if (flags & DB_MULTIPLE) { + if (data.get_data() == 0) { + ret = __os_umalloc(dbp->get_DB()->dbenv, + dataulen, &tmpdata); + if (ret != 0) + goto err; + data.set_data(tmpdata); + bulk_alloc = 1; + } + dataflags |= DB_DBT_USERMEM; + } else + dataflags |= DB_DBT_MALLOC; + data.set_flags(dataflags); + + /* Got all our stuff, now do the get */ + ret = dbp->get(txnp, &key, &data, flags); + /* + * Otherwise just status. + */ + if (ret == 0) { + /* + * XXX + * We need to xdr_free whatever we are returning, next time. + * However, DB does not allocate a new key if one was given + * and we'd be free'ing up space allocated in the request. + * So, allocate a new key/data pointer if it is the same one + * as in the request. + */ + *freep = 1; + /* + * Key + */ + key_alloc = 0; + if (key.get_data() == keydata) { + ret = __os_umalloc(dbp->get_DB()->dbenv, + key.get_size(), &replyp->keydata.keydata_val); + if (ret != 0) { + __os_ufree(dbp->get_DB()->dbenv, key.get_data()); + __os_ufree(dbp->get_DB()->dbenv, data.get_data()); + goto err; + } + key_alloc = 1; + memcpy(replyp->keydata.keydata_val, key.get_data(), key.get_size()); + } else + replyp->keydata.keydata_val = (char *)key.get_data(); + + replyp->keydata.keydata_len = key.get_size(); + + /* + * Data + */ + if (data.get_data() == datadata) { + ret = __os_umalloc(dbp->get_DB()->dbenv, + data.get_size(), &replyp->datadata.datadata_val); + if (ret != 0) { + __os_ufree(dbp->get_DB()->dbenv, key.get_data()); + __os_ufree(dbp->get_DB()->dbenv, data.get_data()); + if (key_alloc) + __os_ufree(dbp->get_DB()->dbenv, + replyp->keydata.keydata_val); + goto err; + } + memcpy(replyp->datadata.datadata_val, data.get_data(), + data.get_size()); + } else + replyp->datadata.datadata_val = (char *)data.get_data(); + replyp->datadata.datadata_len = data.get_size(); + } else { +err: replyp->keydata.keydata_val = NULL; + replyp->keydata.keydata_len = 0; + replyp->datadata.datadata_val = NULL; + replyp->datadata.datadata_len = 0; + *freep = 0; + if (bulk_alloc) + __os_ufree(dbp->get_DB()->dbenv, data.get_data()); + } + replyp->status = ret; + return; +} + +/* BEGIN __db_h_ffactor_proc */ +extern "C" void +__db_h_ffactor_proc( + long dbpcl_id, + u_int32_t ffactor, + __db_h_ffactor_reply *replyp) +/* END __db_h_ffactor_proc */ +{ + Db *dbp; + ct_entry *dbp_ctp; + int ret; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + + ret = dbp->set_h_ffactor(ffactor); + + replyp->status = ret; + return; +} + +/* BEGIN __db_h_nelem_proc */ +extern "C" void +__db_h_nelem_proc( + long dbpcl_id, + u_int32_t nelem, + __db_h_nelem_reply *replyp) +/* END __db_h_nelem_proc */ +{ + Db *dbp; + ct_entry *dbp_ctp; + int ret; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + + ret = dbp->set_h_nelem(nelem); + + replyp->status = ret; + return; +} + +/* BEGIN __db_key_range_proc */ +extern "C" void +__db_key_range_proc( + long dbpcl_id, + long txnpcl_id, + u_int32_t keydlen, + u_int32_t keydoff, + u_int32_t keyulen, + u_int32_t keyflags, + void *keydata, + u_int32_t keysize, + u_int32_t flags, + __db_key_range_reply *replyp) +/* END __db_key_range_proc */ +{ + Db *dbp; + DB_KEY_RANGE range; + DbTxn *txnp; + ct_entry *dbp_ctp, *txnp_ctp; + int ret; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + if (txnpcl_id != 0) { + ACTIVATE_CTP(txnp_ctp, txnpcl_id, CT_TXN); + txnp = (DbTxn *)txnp_ctp->ct_anyp; + } else + txnp = NULL; + + /* Set up key */ + Dbt key(keydata, keysize); + key.set_dlen(keydlen); + key.set_ulen(keyulen); + key.set_doff(keydoff); + key.set_flags(keyflags); + + ret = dbp->key_range(txnp, &key, &range, flags); + + replyp->status = ret; + replyp->less = range.less; + replyp->equal = range.equal; + replyp->greater = range.greater; + return; +} + +/* BEGIN __db_lorder_proc */ +extern "C" void +__db_lorder_proc( + long dbpcl_id, + u_int32_t lorder, + __db_lorder_reply *replyp) +/* END __db_lorder_proc */ +{ + Db *dbp; + ct_entry *dbp_ctp; + int ret; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + + ret = dbp->set_lorder(lorder); + + replyp->status = ret; + return; +} + +/* BEGIN __db_open_proc */ +extern "C" void +__db_open_proc( + long dbpcl_id, + long txnpcl_id, + char *name, + char *subdb, + u_int32_t type, + u_int32_t flags, + u_int32_t mode, + __db_open_reply *replyp) +/* END __db_open_proc */ +{ + Db *dbp; + DbTxn *txnp; + DBTYPE dbtype; + ct_entry *dbp_ctp, *new_ctp, *txnp_ctp; + int isswapped, ret; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + if (txnpcl_id != 0) { + ACTIVATE_CTP(txnp_ctp, txnpcl_id, CT_TXN); + txnp = (DbTxn *)txnp_ctp->ct_anyp; + } else + txnp = NULL; + + replyp->dbcl_id = dbpcl_id; + if ((new_ctp = __dbsrv_sharedb(dbp_ctp, name, subdb, (DBTYPE)type, flags)) + != NULL) { + /* + * We can share, clean up old ID, set new one. + */ + if (__dbsrv_verbose) + printf("Sharing db ID %ld\n", new_ctp->ct_id); + replyp->dbcl_id = new_ctp->ct_id; + ret = __db_close_int(dbpcl_id, 0); + goto out; + } + ret = dbp->open(txnp, name, subdb, (DBTYPE)type, flags, mode); + if (ret == 0) { + (void)dbp->get_type(&dbtype); + replyp->type = dbtype; + /* XXX + * Tcl needs to peek at dbp->flags for DB_AM_DUP. Send + * this dbp's flags back. + */ + replyp->dbflags = (int) dbp->get_DB()->flags; + /* + * We need to determine the byte order of the database + * and send it back to the client. Determine it by + * the server's native order and the swapped value of + * the DB itself. + */ + (void)dbp->get_byteswapped(&isswapped); + if (__db_byteorder(NULL, 1234) == 0) { + if (isswapped == 0) + replyp->lorder = 1234; + else + replyp->lorder = 4321; + } else { + if (isswapped == 0) + replyp->lorder = 4321; + else + replyp->lorder = 1234; + } + dbp_ctp->ct_dbdp.type = dbtype; + dbp_ctp->ct_dbdp.dbflags = LF_ISSET(DB_SERVER_DBFLAGS); + if (name == NULL) + dbp_ctp->ct_dbdp.db = NULL; + else if ((ret = __os_strdup(dbp->get_DB()->dbenv, name, + &dbp_ctp->ct_dbdp.db)) != 0) + goto out; + if (subdb == NULL) + dbp_ctp->ct_dbdp.subdb = NULL; + else if ((ret = __os_strdup(dbp->get_DB()->dbenv, subdb, + &dbp_ctp->ct_dbdp.subdb)) != 0) + goto out; + } +out: + replyp->status = ret; + return; +} + +/* BEGIN __db_pagesize_proc */ +extern "C" void +__db_pagesize_proc( + long dbpcl_id, + u_int32_t pagesize, + __db_pagesize_reply *replyp) +/* END __db_pagesize_proc */ +{ + Db *dbp; + ct_entry *dbp_ctp; + int ret; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + + ret = dbp->set_pagesize(pagesize); + + replyp->status = ret; + return; +} + +/* BEGIN __db_pget_proc */ +extern "C" void +__db_pget_proc( + long dbpcl_id, + long txnpcl_id, + u_int32_t skeydlen, + u_int32_t skeydoff, + u_int32_t skeyulen, + u_int32_t skeyflags, + void *skeydata, + u_int32_t skeysize, + u_int32_t pkeydlen, + u_int32_t pkeydoff, + u_int32_t pkeyulen, + u_int32_t pkeyflags, + void *pkeydata, + u_int32_t pkeysize, + u_int32_t datadlen, + u_int32_t datadoff, + u_int32_t dataulen, + u_int32_t dataflags, + void *datadata, + u_int32_t datasize, + u_int32_t flags, + __db_pget_reply *replyp, + int * freep) +/* END __db_pget_proc */ +{ + Db *dbp; + DbTxn *txnp; + ct_entry *dbp_ctp, *txnp_ctp; + int key_alloc, ret; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + if (txnpcl_id != 0) { + ACTIVATE_CTP(txnp_ctp, txnpcl_id, CT_TXN); + txnp = (DbTxn *)txnp_ctp->ct_anyp; + } else + txnp = NULL; + + *freep = 0; + + /* + * Ignore memory related flags on server. + */ + /* Set up key and data */ + Dbt skey(skeydata, skeysize); + skey.set_dlen(skeydlen); + skey.set_ulen(skeyulen); + skey.set_doff(skeydoff); + skey.set_flags(DB_DBT_MALLOC | (skeyflags & DB_DBT_PARTIAL)); + + Dbt pkey(pkeydata, pkeysize); + pkey.set_dlen(pkeydlen); + pkey.set_ulen(pkeyulen); + pkey.set_doff(pkeydoff); + pkey.set_flags(DB_DBT_MALLOC | (pkeyflags & DB_DBT_PARTIAL)); + + Dbt data(datadata, datasize); + data.set_dlen(datadlen); + data.set_ulen(dataulen); + data.set_doff(datadoff); + data.set_flags(DB_DBT_MALLOC | (dataflags & DB_DBT_PARTIAL)); + + /* Got all our stuff, now do the get */ + ret = dbp->pget(txnp, &skey, &pkey, &data, flags); + /* + * Otherwise just status. + */ + if (ret == 0) { + /* + * XXX + * We need to xdr_free whatever we are returning, next time. + * However, DB does not allocate a new key if one was given + * and we'd be free'ing up space allocated in the request. + * So, allocate a new key/data pointer if it is the same one + * as in the request. + */ + *freep = 1; + /* + * Key + */ + key_alloc = 0; + if (skey.get_data() == skeydata) { + ret = __os_umalloc(dbp->get_DB()->dbenv, + skey.get_size(), &replyp->skeydata.skeydata_val); + if (ret != 0) { + __os_ufree(dbp->get_DB()->dbenv, skey.get_data()); + __os_ufree(dbp->get_DB()->dbenv, pkey.get_data()); + __os_ufree(dbp->get_DB()->dbenv, data.get_data()); + goto err; + } + key_alloc = 1; + memcpy(replyp->skeydata.skeydata_val, skey.get_data(), + skey.get_size()); + } else + replyp->skeydata.skeydata_val = (char *)skey.get_data(); + + replyp->skeydata.skeydata_len = skey.get_size(); + + /* + * Primary key + */ + if (pkey.get_data() == pkeydata) { + ret = __os_umalloc(dbp->get_DB()->dbenv, + pkey.get_size(), &replyp->pkeydata.pkeydata_val); + if (ret != 0) { + __os_ufree(dbp->get_DB()->dbenv, skey.get_data()); + __os_ufree(dbp->get_DB()->dbenv, pkey.get_data()); + __os_ufree(dbp->get_DB()->dbenv, data.get_data()); + if (key_alloc) + __os_ufree(dbp->get_DB()->dbenv, + replyp->skeydata.skeydata_val); + goto err; + } + /* + * We can set it to 2, because they cannot send the + * pkey over without sending the skey over too. + * So if they did send a pkey, they must have sent + * the skey as well. + */ + key_alloc = 2; + memcpy(replyp->pkeydata.pkeydata_val, pkey.get_data(), + pkey.get_size()); + } else + replyp->pkeydata.pkeydata_val = (char *)pkey.get_data(); + replyp->pkeydata.pkeydata_len = pkey.get_size(); + + /* + * Data + */ + if (data.get_data() == datadata) { + ret = __os_umalloc(dbp->get_DB()->dbenv, + data.get_size(), &replyp->datadata.datadata_val); + if (ret != 0) { + __os_ufree(dbp->get_DB()->dbenv, skey.get_data()); + __os_ufree(dbp->get_DB()->dbenv, pkey.get_data()); + __os_ufree(dbp->get_DB()->dbenv, data.get_data()); + /* + * If key_alloc is 1, just skey needs to be + * freed, if key_alloc is 2, both skey and pkey + * need to be freed. + */ + if (key_alloc--) + __os_ufree(dbp->get_DB()->dbenv, + replyp->skeydata.skeydata_val); + if (key_alloc) + __os_ufree(dbp->get_DB()->dbenv, + replyp->pkeydata.pkeydata_val); + goto err; + } + memcpy(replyp->datadata.datadata_val, data.get_data(), + data.get_size()); + } else + replyp->datadata.datadata_val = (char *)data.get_data(); + replyp->datadata.datadata_len = data.get_size(); + } else { +err: replyp->skeydata.skeydata_val = NULL; + replyp->skeydata.skeydata_len = 0; + replyp->pkeydata.pkeydata_val = NULL; + replyp->pkeydata.pkeydata_len = 0; + replyp->datadata.datadata_val = NULL; + replyp->datadata.datadata_len = 0; + *freep = 0; + } + replyp->status = ret; + return; +} + +/* BEGIN __db_put_proc */ +extern "C" void +__db_put_proc( + long dbpcl_id, + long txnpcl_id, + u_int32_t keydlen, + u_int32_t keydoff, + u_int32_t keyulen, + u_int32_t keyflags, + void *keydata, + u_int32_t keysize, + u_int32_t datadlen, + u_int32_t datadoff, + u_int32_t dataulen, + u_int32_t dataflags, + void *datadata, + u_int32_t datasize, + u_int32_t flags, + __db_put_reply *replyp, + int * freep) +/* END __db_put_proc */ +{ + Db *dbp; + DbTxn *txnp; + ct_entry *dbp_ctp, *txnp_ctp; + int ret; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + if (txnpcl_id != 0) { + ACTIVATE_CTP(txnp_ctp, txnpcl_id, CT_TXN); + txnp = (DbTxn *)txnp_ctp->ct_anyp; + } else + txnp = NULL; + + *freep = 0; + + /* Set up key and data */ + Dbt key(keydata, keysize); + key.set_dlen(keydlen); + key.set_ulen(keyulen); + key.set_doff(keydoff); + key.set_flags(DB_DBT_MALLOC | (keyflags & DB_DBT_PARTIAL)); + + Dbt data(datadata, datasize); + data.set_dlen(datadlen); + data.set_ulen(dataulen); + data.set_doff(datadoff); + data.set_flags(dataflags); + + /* Got all our stuff, now do the put */ + ret = dbp->put(txnp, &key, &data, flags); + /* + * If the client did a DB_APPEND, set up key in reply. + * Otherwise just status. + */ + if (ret == 0 && (flags == DB_APPEND)) { + /* + * XXX + * We need to xdr_free whatever we are returning, next time. + * However, DB does not allocate a new key if one was given + * and we'd be free'ing up space allocated in the request. + * So, allocate a new key/data pointer if it is the same one + * as in the request. + */ + *freep = 1; + /* + * Key + */ + if (key.get_data() == keydata) { + ret = __os_umalloc(dbp->get_DB()->dbenv, + key.get_size(), &replyp->keydata.keydata_val); + if (ret != 0) { + __os_ufree(dbp->get_DB()->dbenv, key.get_data()); + goto err; + } + memcpy(replyp->keydata.keydata_val, key.get_data(), key.get_size()); + } else + replyp->keydata.keydata_val = (char *)key.get_data(); + + replyp->keydata.keydata_len = key.get_size(); + } else { +err: replyp->keydata.keydata_val = NULL; + replyp->keydata.keydata_len = 0; + *freep = 0; + } + replyp->status = ret; + return; +} + +/* BEGIN __db_re_delim_proc */ +extern "C" void +__db_re_delim_proc( + long dbpcl_id, + u_int32_t delim, + __db_re_delim_reply *replyp) +/* END __db_re_delim_proc */ +{ + Db *dbp; + ct_entry *dbp_ctp; + int ret; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + + ret = dbp->set_re_delim(delim); + + replyp->status = ret; + return; +} + +/* BEGIN __db_re_len_proc */ +extern "C" void +__db_re_len_proc( + long dbpcl_id, + u_int32_t len, + __db_re_len_reply *replyp) +/* END __db_re_len_proc */ +{ + Db *dbp; + ct_entry *dbp_ctp; + int ret; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + + ret = dbp->set_re_len(len); + + replyp->status = ret; + return; +} + +/* BEGIN __db_re_pad_proc */ +extern "C" void +__db_re_pad_proc( + long dbpcl_id, + u_int32_t pad, + __db_re_pad_reply *replyp) +/* END __db_re_pad_proc */ +{ + Db *dbp; + ct_entry *dbp_ctp; + int ret; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + + ret = dbp->set_re_pad(pad); + + replyp->status = ret; + return; +} + +/* BEGIN __db_remove_proc */ +extern "C" void +__db_remove_proc( + long dbpcl_id, + char *name, + char *subdb, + u_int32_t flags, + __db_remove_reply *replyp) +/* END __db_remove_proc */ +{ + Db *dbp; + ct_entry *dbp_ctp; + int ret; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + + ret = dbp->remove(name, subdb, flags); + __dbdel_ctp(dbp_ctp); + + replyp->status = ret; + return; +} + +/* BEGIN __db_rename_proc */ +extern "C" void +__db_rename_proc( + long dbpcl_id, + char *name, + char *subdb, + char *newname, + u_int32_t flags, + __db_rename_reply *replyp) +/* END __db_rename_proc */ +{ + Db *dbp; + ct_entry *dbp_ctp; + int ret; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + + ret = dbp->rename(name, subdb, newname, flags); + __dbdel_ctp(dbp_ctp); + + replyp->status = ret; + return; +} + +/* BEGIN __db_stat_proc */ +extern "C" void +__db_stat_proc( + long dbpcl_id, + u_int32_t flags, + __db_stat_reply *replyp, + int * freep) +/* END __db_stat_proc */ +{ + Db *dbp; + DBTYPE type; + ct_entry *dbp_ctp; + u_int32_t *q, *p, *retsp; + int i, len, ret; + void *sp; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + + ret = dbp->stat(&sp, flags); + replyp->status = ret; + if (ret != 0) + return; + /* + * We get here, we have success. Allocate an array so that + * we can use the list generator. Generate the reply, free + * up the space. + */ + /* + * XXX This assumes that all elements of all stat structures + * are u_int32_t fields. They are, currently. + */ + (void)dbp->get_type(&type); + if (type == DB_HASH) + len = sizeof(DB_HASH_STAT); + else if (type == DB_QUEUE) + len = sizeof(DB_QUEUE_STAT); + else /* BTREE or RECNO are same stats */ + len = sizeof(DB_BTREE_STAT); + replyp->stats.stats_len = len / sizeof(u_int32_t); + + if ((ret = __os_umalloc(dbp->get_DB()->dbenv, + len * replyp->stats.stats_len, &retsp)) != 0) + goto out; + for (i = 0, q = retsp, p = (u_int32_t *)sp; i < len; + i++, q++, p++) + *q = *p; + replyp->stats.stats_val = retsp; + __os_ufree(dbp->get_DB()->dbenv, sp); + if (ret == 0) + *freep = 1; +out: + replyp->status = ret; + return; +} + +/* BEGIN __db_sync_proc */ +extern "C" void +__db_sync_proc( + long dbpcl_id, + u_int32_t flags, + __db_sync_reply *replyp) +/* END __db_sync_proc */ +{ + Db *dbp; + ct_entry *dbp_ctp; + int ret; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + + ret = dbp->sync(flags); + + replyp->status = ret; + return; +} + +/* BEGIN __db_truncate_proc */ +extern "C" void +__db_truncate_proc( + long dbpcl_id, + long txnpcl_id, + u_int32_t flags, + __db_truncate_reply *replyp) +/* END __db_truncate_proc */ +{ + Db *dbp; + DbTxn *txnp; + ct_entry *dbp_ctp, *txnp_ctp; + u_int32_t count; + int ret; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + if (txnpcl_id != 0) { + ACTIVATE_CTP(txnp_ctp, txnpcl_id, CT_TXN); + txnp = (DbTxn *)txnp_ctp->ct_anyp; + } else + txnp = NULL; + + ret = dbp->truncate(txnp, &count, flags); + replyp->status = ret; + if (ret == 0) + replyp->count = count; + return; +} + +/* BEGIN __db_cursor_proc */ +extern "C" void +__db_cursor_proc( + long dbpcl_id, + long txnpcl_id, + u_int32_t flags, + __db_cursor_reply *replyp) +/* END __db_cursor_proc */ +{ + Db *dbp; + Dbc *dbc; + DbTxn *txnp; + ct_entry *dbc_ctp, *env_ctp, *dbp_ctp, *txnp_ctp; + int ret; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + dbc_ctp = new_ct_ent(&replyp->status); + if (dbc_ctp == NULL) + return; + + if (txnpcl_id != 0) { + ACTIVATE_CTP(txnp_ctp, txnpcl_id, CT_TXN); + txnp = (DbTxn *)txnp_ctp->ct_anyp; + dbc_ctp->ct_activep = txnp_ctp->ct_activep; + } else + txnp = NULL; + + if ((ret = dbp->cursor(txnp, &dbc, flags)) == 0) { + dbc_ctp->ct_dbc = dbc; + dbc_ctp->ct_type = CT_CURSOR; + dbc_ctp->ct_parent = dbp_ctp; + env_ctp = dbp_ctp->ct_envparent; + dbc_ctp->ct_envparent = env_ctp; + __dbsrv_settimeout(dbc_ctp, env_ctp->ct_timeout); + __dbsrv_active(dbc_ctp); + replyp->dbcidcl_id = dbc_ctp->ct_id; + } else + __dbclear_ctp(dbc_ctp); + + replyp->status = ret; + return; +} + +/* BEGIN __db_join_proc */ +extern "C" void +__db_join_proc( + long dbpcl_id, + u_int32_t *curs, + u_int32_t curslen, + u_int32_t flags, + __db_join_reply *replyp) +/* END __db_join_proc */ +{ + Db *dbp; + Dbc **jcurs, **c; + Dbc *dbc; + ct_entry *dbc_ctp, *ctp, *dbp_ctp; + size_t size; + u_int32_t *cl, i; + int ret; + + ACTIVATE_CTP(dbp_ctp, dbpcl_id, CT_DB); + dbp = (Db *)dbp_ctp->ct_anyp; + + dbc_ctp = new_ct_ent(&replyp->status); + if (dbc_ctp == NULL) + return; + + size = (curslen + 1) * sizeof(Dbc *); + if ((ret = __os_calloc(dbp->get_DB()->dbenv, + curslen + 1, sizeof(Dbc *), &jcurs)) != 0) { + replyp->status = ret; + __dbclear_ctp(dbc_ctp); + return; + } + /* + * If our curslist has a parent txn, we need to use it too + * for the activity timeout. All cursors must be part of + * the same transaction, so just check the first. + */ + ctp = get_tableent(*curs); + DB_ASSERT(ctp->ct_type == CT_CURSOR); + /* + * If we are using a transaction, set the join activity timer + * to point to the parent transaction. + */ + if (ctp->ct_activep != &ctp->ct_active) + dbc_ctp->ct_activep = ctp->ct_activep; + for (i = 0, cl = curs, c = jcurs; i < curslen; i++, cl++, c++) { + ctp = get_tableent(*cl); + if (ctp == NULL) { + replyp->status = DB_NOSERVER_ID; + goto out; + } + /* + * If we are using a txn, the join cursor points to the + * transaction timeout. If we are not using a transaction, + * then all the curslist cursors must point to the join + * cursor's timeout so that we do not timeout any of the + * curlist cursors while the join cursor is active. + * Change the type of the curslist ctps to CT_JOIN so that + * we know they are part of a join list and we can distinguish + * them and later restore them when the join cursor is closed. + */ + DB_ASSERT(ctp->ct_type == CT_CURSOR); + ctp->ct_type |= CT_JOIN; + ctp->ct_origp = ctp->ct_activep; + /* + * Setting this to the ct_active field of the dbc_ctp is + * really just a way to distinguish which join dbc this + * cursor is part of. The ct_activep of this cursor is + * not used at all during its lifetime as part of a join + * cursor. + */ + ctp->ct_activep = &dbc_ctp->ct_active; + *c = ctp->ct_dbc; + } + *c = NULL; + if ((ret = dbp->join(jcurs, &dbc, flags)) == 0) { + dbc_ctp->ct_dbc = dbc; + dbc_ctp->ct_type = (CT_JOINCUR | CT_CURSOR); + dbc_ctp->ct_parent = dbp_ctp; + dbc_ctp->ct_envparent = dbp_ctp->ct_envparent; + __dbsrv_settimeout(dbc_ctp, dbp_ctp->ct_envparent->ct_timeout); + __dbsrv_active(dbc_ctp); + replyp->dbcidcl_id = dbc_ctp->ct_id; + } else { + __dbclear_ctp(dbc_ctp); + /* + * If we get an error, undo what we did above to any cursors. + */ + for (cl = curs; *cl != 0; cl++) { + ctp = get_tableent(*cl); + ctp->ct_type = CT_CURSOR; + ctp->ct_activep = ctp->ct_origp; + } + } + + replyp->status = ret; +out: + __os_free(dbp->get_DB()->dbenv, jcurs); + return; +} + +/* BEGIN __dbc_close_proc */ +extern "C" void +__dbc_close_proc( + long dbccl_id, + __dbc_close_reply *replyp) +/* END __dbc_close_proc */ +{ + ct_entry *dbc_ctp; + + ACTIVATE_CTP(dbc_ctp, dbccl_id, CT_CURSOR); + replyp->status = __dbc_close_int(dbc_ctp); + return; +} + +/* BEGIN __dbc_count_proc */ +extern "C" void +__dbc_count_proc( + long dbccl_id, + u_int32_t flags, + __dbc_count_reply *replyp) +/* END __dbc_count_proc */ +{ + Dbc *dbc; + ct_entry *dbc_ctp; + db_recno_t num; + int ret; + + ACTIVATE_CTP(dbc_ctp, dbccl_id, CT_CURSOR); + dbc = (Dbc *)dbc_ctp->ct_anyp; + + ret = dbc->count(&num, flags); + replyp->status = ret; + if (ret == 0) + replyp->dupcount = num; + return; +} + +/* BEGIN __dbc_del_proc */ +extern "C" void +__dbc_del_proc( + long dbccl_id, + u_int32_t flags, + __dbc_del_reply *replyp) +/* END __dbc_del_proc */ +{ + Dbc *dbc; + ct_entry *dbc_ctp; + int ret; + + ACTIVATE_CTP(dbc_ctp, dbccl_id, CT_CURSOR); + dbc = (Dbc *)dbc_ctp->ct_anyp; + + ret = dbc->del(flags); + + replyp->status = ret; + return; +} + +/* BEGIN __dbc_dup_proc */ +extern "C" void +__dbc_dup_proc( + long dbccl_id, + u_int32_t flags, + __dbc_dup_reply *replyp) +/* END __dbc_dup_proc */ +{ + Dbc *dbc, *newdbc; + ct_entry *dbc_ctp, *new_ctp; + int ret; + + ACTIVATE_CTP(dbc_ctp, dbccl_id, CT_CURSOR); + dbc = (Dbc *)dbc_ctp->ct_anyp; + + new_ctp = new_ct_ent(&replyp->status); + if (new_ctp == NULL) + return; + + if ((ret = dbc->dup(&newdbc, flags)) == 0) { + new_ctp->ct_dbc = newdbc; + new_ctp->ct_type = CT_CURSOR; + new_ctp->ct_parent = dbc_ctp->ct_parent; + new_ctp->ct_envparent = dbc_ctp->ct_envparent; + /* + * If our cursor has a parent txn, we need to use it too. + */ + if (dbc_ctp->ct_activep != &dbc_ctp->ct_active) + new_ctp->ct_activep = dbc_ctp->ct_activep; + __dbsrv_settimeout(new_ctp, dbc_ctp->ct_timeout); + __dbsrv_active(new_ctp); + replyp->dbcidcl_id = new_ctp->ct_id; + } else + __dbclear_ctp(new_ctp); + + replyp->status = ret; + return; +} + +/* BEGIN __dbc_get_proc */ +extern "C" void +__dbc_get_proc( + long dbccl_id, + u_int32_t keydlen, + u_int32_t keydoff, + u_int32_t keyulen, + u_int32_t keyflags, + void *keydata, + u_int32_t keysize, + u_int32_t datadlen, + u_int32_t datadoff, + u_int32_t dataulen, + u_int32_t dataflags, + void *datadata, + u_int32_t datasize, + u_int32_t flags, + __dbc_get_reply *replyp, + int * freep) +/* END __dbc_get_proc */ +{ + Dbc *dbc; + DbEnv *dbenv; + ct_entry *dbc_ctp; + int key_alloc, bulk_alloc, ret; + void *tmpdata; + + ACTIVATE_CTP(dbc_ctp, dbccl_id, CT_CURSOR); + dbc = (Dbc *)dbc_ctp->ct_anyp; + dbenv = DbEnv::get_DbEnv(((DBC *)dbc)->dbp->dbenv); + + *freep = 0; + bulk_alloc = 0; + + /* Set up key and data */ + Dbt key(keydata, keysize); + key.set_dlen(keydlen); + key.set_ulen(keyulen); + key.set_doff(keydoff); + key.set_flags(DB_DBT_MALLOC | (keyflags & DB_DBT_PARTIAL)); + + Dbt data(datadata, datasize); + data.set_dlen(datadlen); + data.set_ulen(dataulen); + data.set_doff(datadoff); + dataflags &= DB_DBT_PARTIAL; + if (flags & DB_MULTIPLE || flags & DB_MULTIPLE_KEY) { + if (data.get_data() == NULL) { + ret = __os_umalloc(dbenv->get_DB_ENV(), + data.get_ulen(), &tmpdata); + if (ret != 0) + goto err; + data.set_data(tmpdata); + bulk_alloc = 1; + } + dataflags |= DB_DBT_USERMEM; + } else + dataflags |= DB_DBT_MALLOC; + data.set_flags(dataflags); + + /* Got all our stuff, now do the get */ + ret = dbc->get(&key, &data, flags); + + /* + * Otherwise just status. + */ + if (ret == 0) { + /* + * XXX + * We need to xdr_free whatever we are returning, next time. + * However, DB does not allocate a new key if one was given + * and we'd be free'ing up space allocated in the request. + * So, allocate a new key/data pointer if it is the same one + * as in the request. + */ + *freep = 1; + /* + * Key + */ + key_alloc = 0; + if (key.get_data() == keydata) { + ret = __os_umalloc(dbenv->get_DB_ENV(), key.get_size(), + &replyp->keydata.keydata_val); + if (ret != 0) { + __os_ufree(dbenv->get_DB_ENV(), key.get_data()); + __os_ufree(dbenv->get_DB_ENV(), data.get_data()); + goto err; + } + key_alloc = 1; + memcpy(replyp->keydata.keydata_val, key.get_data(), key.get_size()); + } else + replyp->keydata.keydata_val = (char *)key.get_data(); + + replyp->keydata.keydata_len = key.get_size(); + + /* + * Data + */ + if (data.get_data() == datadata) { + ret = __os_umalloc(dbenv->get_DB_ENV(), data.get_size(), + &replyp->datadata.datadata_val); + if (ret != 0) { + __os_ufree(dbenv->get_DB_ENV(), key.get_data()); + __os_ufree(dbenv->get_DB_ENV(), data.get_data()); + if (key_alloc) + __os_ufree(dbenv->get_DB_ENV(), + replyp->keydata.keydata_val); + goto err; + } + memcpy(replyp->datadata.datadata_val, data.get_data(), + data.get_size()); + } else + replyp->datadata.datadata_val = (char *)data.get_data(); + replyp->datadata.datadata_len = data.get_size(); + } else { +err: replyp->keydata.keydata_val = NULL; + replyp->keydata.keydata_len = 0; + replyp->datadata.datadata_val = NULL; + replyp->datadata.datadata_len = 0; + *freep = 0; + if (bulk_alloc) + __os_ufree(dbenv->get_DB_ENV(), data.get_data()); + } + replyp->status = ret; + return; +} + +/* BEGIN __dbc_pget_proc */ +extern "C" void +__dbc_pget_proc( + long dbccl_id, + u_int32_t skeydlen, + u_int32_t skeydoff, + u_int32_t skeyulen, + u_int32_t skeyflags, + void *skeydata, + u_int32_t skeysize, + u_int32_t pkeydlen, + u_int32_t pkeydoff, + u_int32_t pkeyulen, + u_int32_t pkeyflags, + void *pkeydata, + u_int32_t pkeysize, + u_int32_t datadlen, + u_int32_t datadoff, + u_int32_t dataulen, + u_int32_t dataflags, + void *datadata, + u_int32_t datasize, + u_int32_t flags, + __dbc_pget_reply *replyp, + int * freep) +/* END __dbc_pget_proc */ +{ + Dbc *dbc; + DbEnv *dbenv; + ct_entry *dbc_ctp; + int key_alloc, ret; + + ACTIVATE_CTP(dbc_ctp, dbccl_id, CT_CURSOR); + dbc = (Dbc *)dbc_ctp->ct_anyp; + dbenv = DbEnv::get_DbEnv(((DBC *)dbc)->dbp->dbenv); + + *freep = 0; + + /* + * Ignore memory related flags on server. + */ + /* Set up key and data */ + Dbt skey(skeydata, skeysize); + skey.set_dlen(skeydlen); + skey.set_ulen(skeyulen); + skey.set_doff(skeydoff); + skey.set_flags(DB_DBT_MALLOC | (skeyflags & DB_DBT_PARTIAL)); + + Dbt pkey(pkeydata, pkeysize); + pkey.set_dlen(pkeydlen); + pkey.set_ulen(pkeyulen); + pkey.set_doff(pkeydoff); + pkey.set_flags(DB_DBT_MALLOC | (pkeyflags & DB_DBT_PARTIAL)); + + Dbt data(datadata, datasize); + data.set_dlen(datadlen); + data.set_ulen(dataulen); + data.set_doff(datadoff); + data.set_flags(DB_DBT_MALLOC | (dataflags & DB_DBT_PARTIAL)); + + /* Got all our stuff, now do the get */ + ret = dbc->pget(&skey, &pkey, &data, flags); + /* + * Otherwise just status. + */ + if (ret == 0) { + /* + * XXX + * We need to xdr_free whatever we are returning, next time. + * However, DB does not allocate a new key if one was given + * and we'd be free'ing up space allocated in the request. + * So, allocate a new key/data pointer if it is the same one + * as in the request. + */ + *freep = 1; + /* + * Key + */ + key_alloc = 0; + if (skey.get_data() == skeydata) { + ret = __os_umalloc(dbenv->get_DB_ENV(), + skey.get_size(), &replyp->skeydata.skeydata_val); + if (ret != 0) { + __os_ufree(dbenv->get_DB_ENV(), skey.get_data()); + __os_ufree(dbenv->get_DB_ENV(), pkey.get_data()); + __os_ufree(dbenv->get_DB_ENV(), data.get_data()); + goto err; + } + key_alloc = 1; + memcpy(replyp->skeydata.skeydata_val, skey.get_data(), + skey.get_size()); + } else + replyp->skeydata.skeydata_val = (char *)skey.get_data(); + replyp->skeydata.skeydata_len = skey.get_size(); + + /* + * Primary key + */ + if (pkey.get_data() == pkeydata) { + ret = __os_umalloc(dbenv->get_DB_ENV(), + pkey.get_size(), &replyp->pkeydata.pkeydata_val); + if (ret != 0) { + __os_ufree(dbenv->get_DB_ENV(), skey.get_data()); + __os_ufree(dbenv->get_DB_ENV(), pkey.get_data()); + __os_ufree(dbenv->get_DB_ENV(), data.get_data()); + if (key_alloc) + __os_ufree(dbenv->get_DB_ENV(), + replyp->skeydata.skeydata_val); + goto err; + } + /* + * We can set it to 2, because they cannot send the + * pkey over without sending the skey over too. + * So if they did send a pkey, they must have sent + * the skey as well. + */ + key_alloc = 2; + memcpy(replyp->pkeydata.pkeydata_val, pkey.get_data(), + pkey.get_size()); + } else + replyp->pkeydata.pkeydata_val = (char *)pkey.get_data(); + replyp->pkeydata.pkeydata_len = pkey.get_size(); + + /* + * Data + */ + if (data.get_data() == datadata) { + ret = __os_umalloc(dbenv->get_DB_ENV(), + data.get_size(), &replyp->datadata.datadata_val); + if (ret != 0) { + __os_ufree(dbenv->get_DB_ENV(), skey.get_data()); + __os_ufree(dbenv->get_DB_ENV(), pkey.get_data()); + __os_ufree(dbenv->get_DB_ENV(), data.get_data()); + /* + * If key_alloc is 1, just skey needs to be + * freed, if key_alloc is 2, both skey and pkey + * need to be freed. + */ + if (key_alloc--) + __os_ufree(dbenv->get_DB_ENV(), + replyp->skeydata.skeydata_val); + if (key_alloc) + __os_ufree(dbenv->get_DB_ENV(), + replyp->pkeydata.pkeydata_val); + goto err; + } + memcpy(replyp->datadata.datadata_val, data.get_data(), + data.get_size()); + } else + replyp->datadata.datadata_val = (char *)data.get_data(); + replyp->datadata.datadata_len = data.get_size(); + } else { +err: replyp->skeydata.skeydata_val = NULL; + replyp->skeydata.skeydata_len = 0; + replyp->pkeydata.pkeydata_val = NULL; + replyp->pkeydata.pkeydata_len = 0; + replyp->datadata.datadata_val = NULL; + replyp->datadata.datadata_len = 0; + *freep = 0; + } + replyp->status = ret; + return; +} + +/* BEGIN __dbc_put_proc */ +extern "C" void +__dbc_put_proc( + long dbccl_id, + u_int32_t keydlen, + u_int32_t keydoff, + u_int32_t keyulen, + u_int32_t keyflags, + void *keydata, + u_int32_t keysize, + u_int32_t datadlen, + u_int32_t datadoff, + u_int32_t dataulen, + u_int32_t dataflags, + void *datadata, + u_int32_t datasize, + u_int32_t flags, + __dbc_put_reply *replyp, + int * freep) +/* END __dbc_put_proc */ +{ + Db *dbp; + Dbc *dbc; + ct_entry *dbc_ctp; + int ret; + DBTYPE dbtype; + + ACTIVATE_CTP(dbc_ctp, dbccl_id, CT_CURSOR); + dbc = (Dbc *)dbc_ctp->ct_anyp; + dbp = (Db *)dbc_ctp->ct_parent->ct_anyp; + + /* Set up key and data */ + Dbt key(keydata, keysize); + key.set_dlen(keydlen); + key.set_ulen(keyulen); + key.set_doff(keydoff); + /* + * Ignore memory related flags on server. + */ + key.set_flags(DB_DBT_MALLOC | (keyflags & DB_DBT_PARTIAL)); + + Dbt data(datadata, datasize); + data.set_dlen(datadlen); + data.set_ulen(dataulen); + data.set_doff(datadoff); + data.set_flags(dataflags); + + /* Got all our stuff, now do the put */ + ret = dbc->put(&key, &data, flags); + + *freep = 0; + replyp->keydata.keydata_val = NULL; + replyp->keydata.keydata_len = 0; + if (ret == 0 && (flags == DB_AFTER || flags == DB_BEFORE)) { + ret = dbp->get_type(&dbtype); + if (ret == 0 && dbtype == DB_RECNO) { + /* + * We need to xdr_free whatever we are returning, next time. + */ + replyp->keydata.keydata_val = (char *)key.get_data(); + replyp->keydata.keydata_len = key.get_size(); + } + } + replyp->status = ret; + return; +} +#endif /* HAVE_RPC */ diff --git a/db/rpc_server/cxx/db_server_cxxutil.cpp b/db/rpc_server/cxx/db_server_cxxutil.cpp new file mode 100644 index 000000000..404440d48 --- /dev/null +++ b/db/rpc_server/cxx/db_server_cxxutil.cpp @@ -0,0 +1,746 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2002 + * Sleepycat Software. All rights reserved. + */ + +#include "db_config.h" + +#ifndef lint +static const char revid[] = "Id: db_server_cxxutil.cpp,v 1.8 2002/05/23 07:49:34 mjc Exp "; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#if TIME_WITH_SYS_TIME +#include <sys/time.h> +#include <time.h> +#else +#if HAVE_SYS_TIME_H +#include <sys/time.h> +#else +#include <time.h> +#endif +#endif + +#include <rpc/rpc.h> + +#include <limits.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#endif +#include "dbinc_auto/db_server.h" + +#include "db_int.h" +#include "db_cxx.h" +#include "dbinc_auto/clib_ext.h" + +extern "C" { +#include "dbinc/db_server_int.h" +#include "dbinc_auto/rpc_server_ext.h" +#include "dbinc_auto/common_ext.h" + +extern int __dbsrv_main __P((void)); +} + +static int add_home __P((char *)); +static int add_passwd __P((char *)); +static int env_recover __P((char *)); +static void __dbclear_child __P((ct_entry *)); + +static LIST_HEAD(cthead, ct_entry) __dbsrv_head; +static LIST_HEAD(homehead, home_entry) __dbsrv_home; +static long __dbsrv_defto = DB_SERVER_TIMEOUT; +static long __dbsrv_maxto = DB_SERVER_MAXTIMEOUT; +static long __dbsrv_idleto = DB_SERVER_IDLETIMEOUT; +static char *logfile = NULL; +static char *prog; + +static void usage __P((char *)); +static void version_check __P((void)); + +int __dbsrv_verbose = 0; + +int +main( + int argc, + char **argv) +{ + extern char *optarg; + CLIENT *cl; + int ch, ret; + char *passwd; + + prog = argv[0]; + + version_check(); + + /* + * Check whether another server is running or not. There + * is a race condition where two servers could be racing to + * register with the portmapper. The goal of this check is to + * forbid running additional servers (like those started from + * the test suite) if the user is already running one. + * + * XXX + * This does not solve nor prevent two servers from being + * started at the same time and running recovery at the same + * time on the same environments. + */ + if ((cl = clnt_create("localhost", + DB_RPC_SERVERPROG, DB_RPC_SERVERVERS, "tcp")) != NULL) { + fprintf(stderr, + "%s: Berkeley DB RPC server already running.\n", prog); + clnt_destroy(cl); + return (EXIT_FAILURE); + } + + LIST_INIT(&__dbsrv_home); + while ((ch = getopt(argc, argv, "h:I:L:P:t:T:Vv")) != EOF) + switch (ch) { + case 'h': + (void)add_home(optarg); + break; + case 'I': + if (__db_getlong(NULL, prog, + optarg, 1, LONG_MAX, &__dbsrv_idleto)) + return (EXIT_FAILURE); + break; + case 'L': + logfile = optarg; + break; + case 'P': + passwd = strdup(optarg); + memset(optarg, 0, strlen(optarg)); + if (passwd == NULL) { + fprintf(stderr, "%s: strdup: %s\n", + prog, strerror(errno)); + return (EXIT_FAILURE); + } + if ((ret = add_passwd(passwd)) != 0) { + fprintf(stderr, "%s: strdup: %s\n", + prog, strerror(ret)); + return (EXIT_FAILURE); + } + break; + case 't': + if (__db_getlong(NULL, prog, + optarg, 1, LONG_MAX, &__dbsrv_defto)) + return (EXIT_FAILURE); + break; + case 'T': + if (__db_getlong(NULL, prog, + optarg, 1, LONG_MAX, &__dbsrv_maxto)) + return (EXIT_FAILURE); + break; + case 'V': + printf("%s\n", db_version(NULL, NULL, NULL)); + return (EXIT_SUCCESS); + case 'v': + __dbsrv_verbose = 1; + break; + default: + usage(prog); + } + /* + * Check default timeout against maximum timeout + */ + if (__dbsrv_defto > __dbsrv_maxto) + __dbsrv_defto = __dbsrv_maxto; + + /* + * Check default timeout against idle timeout + * It would be bad to timeout environments sooner than txns. + */ + if (__dbsrv_defto > __dbsrv_idleto) + fprintf(stderr, + "%s: WARNING: Idle timeout %ld is less than resource timeout %ld\n", + prog, __dbsrv_idleto, __dbsrv_defto); + + LIST_INIT(&__dbsrv_head); + + /* + * If a client crashes during an RPC, our reply to it + * generates a SIGPIPE. Ignore SIGPIPE so we don't exit unnecessarily. + */ +#ifdef SIGPIPE + signal(SIGPIPE, SIG_IGN); +#endif + + if (logfile != NULL && __db_util_logset("berkeley_db_svc", logfile)) + return (EXIT_FAILURE); + + /* + * Now that we are ready to start, run recovery on all the + * environments specified. + */ + if (env_recover(prog) != 0) + return (EXIT_FAILURE); + + /* + * We've done our setup, now call the generated server loop + */ + if (__dbsrv_verbose) + printf("%s: Ready to receive requests\n", prog); + __dbsrv_main(); + + /* NOTREACHED */ + abort(); +} + +static void +usage(char *prog) +{ + fprintf(stderr, "usage: %s %s\n\t%s\n", prog, + "[-Vv] [-h home] [-P passwd]", + "[-I idletimeout] [-L logfile] [-t def_timeout] [-T maxtimeout]"); + exit(EXIT_FAILURE); +} + +static void +version_check() +{ + int v_major, v_minor, v_patch; + + /* Make sure we're loaded with the right version of the DB library. */ + (void)db_version(&v_major, &v_minor, &v_patch); + if (v_major != DB_VERSION_MAJOR || + v_minor != DB_VERSION_MINOR || v_patch != DB_VERSION_PATCH) { + fprintf(stderr, + "%s: version %d.%d.%d doesn't match library version %d.%d.%d\n", + prog, DB_VERSION_MAJOR, DB_VERSION_MINOR, + DB_VERSION_PATCH, v_major, v_minor, v_patch); + exit(EXIT_FAILURE); + } +} + +extern "C" void +__dbsrv_settimeout( + ct_entry *ctp, + u_int32_t to) +{ + if (to > (u_int32_t)__dbsrv_maxto) + ctp->ct_timeout = __dbsrv_maxto; + else if (to <= 0) + ctp->ct_timeout = __dbsrv_defto; + else + ctp->ct_timeout = to; +} + +extern "C" void +__dbsrv_timeout(int force) +{ + static long to_hint = -1; + time_t t; + long to; + ct_entry *ctp, *nextctp; + + if ((t = time(NULL)) == -1) + return; + + /* + * Check hint. If hint is further in the future + * than now, no work to do. + */ + if (!force && to_hint > 0 && t < to_hint) + return; + to_hint = -1; + /* + * Timeout transactions or cursors holding DB resources. + * Do this before timing out envs to properly release resources. + * + * !!! + * We can just loop through this list looking for cursors and txns. + * We do not need to verify txn and cursor relationships at this + * point because we maintain the list in LIFO order *and* we + * maintain activity in the ultimate txn parent of any cursor + * so either everything in a txn is timing out, or nothing. + * So, since we are LIFO, we will correctly close/abort all the + * appropriate handles, in the correct order. + */ + for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL; ctp = nextctp) { + nextctp = LIST_NEXT(ctp, entries); + switch (ctp->ct_type) { + case CT_TXN: + to = *(ctp->ct_activep) + ctp->ct_timeout; + /* TIMEOUT */ + if (to < t) { + if (__dbsrv_verbose) + printf("Timing out txn id %ld\n", + ctp->ct_id); + (void)((DbTxn *)ctp->ct_anyp)->abort(); + __dbdel_ctp(ctp); + /* + * If we timed out an txn, we may have closed + * all sorts of ctp's. + * So start over with a guaranteed good ctp. + */ + nextctp = LIST_FIRST(&__dbsrv_head); + } else if ((to_hint > 0 && to_hint > to) || + to_hint == -1) + to_hint = to; + break; + case CT_CURSOR: + case (CT_JOINCUR | CT_CURSOR): + to = *(ctp->ct_activep) + ctp->ct_timeout; + /* TIMEOUT */ + if (to < t) { + if (__dbsrv_verbose) + printf("Timing out cursor %ld\n", + ctp->ct_id); + (void)__dbc_close_int(ctp); + /* + * Start over with a guaranteed good ctp. + */ + nextctp = LIST_FIRST(&__dbsrv_head); + } else if ((to_hint > 0 && to_hint > to) || + to_hint == -1) + to_hint = to; + break; + default: + break; + } + } + /* + * Timeout idle handles. + * If we are forcing a timeout, we'll close all env handles. + */ + for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL; ctp = nextctp) { + nextctp = LIST_NEXT(ctp, entries); + if (ctp->ct_type != CT_ENV) + continue; + to = *(ctp->ct_activep) + ctp->ct_idle; + /* TIMEOUT */ + if (to < t || force) { + if (__dbsrv_verbose) + printf("Timing out env id %ld\n", ctp->ct_id); + (void)__dbenv_close_int(ctp->ct_id, 0, 1); + /* + * If we timed out an env, we may have closed + * all sorts of ctp's (maybe even all of them. + * So start over with a guaranteed good ctp. + */ + nextctp = LIST_FIRST(&__dbsrv_head); + } + } +} + +/* + * RECURSIVE FUNCTION. We need to clear/free any number of levels of nested + * layers. + */ +static void +__dbclear_child(ct_entry *parent) +{ + ct_entry *ctp, *nextctp; + + for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL; + ctp = nextctp) { + nextctp = LIST_NEXT(ctp, entries); + if (ctp->ct_type == 0) + continue; + if (ctp->ct_parent == parent) { + __dbclear_child(ctp); + /* + * Need to do this here because le_next may + * have changed with the recursive call and we + * don't want to point to a removed entry. + */ + nextctp = LIST_NEXT(ctp, entries); + __dbclear_ctp(ctp); + } + } +} + +extern "C" void +__dbclear_ctp(ct_entry *ctp) +{ + LIST_REMOVE(ctp, entries); + __os_free(NULL, ctp); +} + +extern "C" void +__dbdel_ctp(ct_entry *parent) +{ + __dbclear_child(parent); + __dbclear_ctp(parent); +} + +extern "C" ct_entry * +new_ct_ent(int *errp) +{ + time_t t; + ct_entry *ctp, *octp; + int ret; + + if ((ret = __os_malloc(NULL, sizeof(ct_entry), &ctp)) != 0) { + *errp = ret; + return (NULL); + } + memset(ctp, 0, sizeof(ct_entry)); + /* + * Get the time as ID. We may service more than one request per + * second however. If we are, then increment id value until we + * find an unused one. We insert entries in LRU fashion at the + * head of the list. So, if the first entry doesn't match, then + * we know for certain that we can use our entry. + */ + if ((t = time(NULL)) == -1) { + *errp = __os_get_errno(); + __os_free(NULL, ctp); + return (NULL); + } + octp = LIST_FIRST(&__dbsrv_head); + if (octp != NULL && octp->ct_id >= t) + t = octp->ct_id + 1; + ctp->ct_id = t; + ctp->ct_idle = __dbsrv_idleto; + ctp->ct_activep = &ctp->ct_active; + ctp->ct_origp = NULL; + ctp->ct_refcount = 1; + + LIST_INSERT_HEAD(&__dbsrv_head, ctp, entries); + return (ctp); +} + +extern "C" ct_entry * +get_tableent(long id) +{ + ct_entry *ctp; + + for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL; + ctp = LIST_NEXT(ctp, entries)) + if (ctp->ct_id == id) + return (ctp); + return (NULL); +} + +extern "C" ct_entry * +__dbsrv_sharedb(ct_entry *db_ctp, const char *name, const char *subdb, DBTYPE type, u_int32_t flags) +{ + ct_entry *ctp; + + /* + * Check if we can share a db handle. Criteria for sharing are: + * If any of the non-sharable flags are set, we cannot share. + * Must be a db ctp, obviously. + * Must share the same env parent. + * Must be the same type, or current one DB_UNKNOWN. + * Must be same byteorder, or current one must not care. + * All flags must match. + * Must be same name, but don't share in-memory databases. + * Must be same subdb name. + */ + if (flags & DB_SERVER_DBNOSHARE) + return (NULL); + for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL; + ctp = LIST_NEXT(ctp, entries)) { + /* + * Skip ourselves. + */ + if (ctp == db_ctp) + continue; + if (ctp->ct_type != CT_DB) + continue; + if (ctp->ct_envparent != db_ctp->ct_envparent) + continue; + if (type != DB_UNKNOWN && ctp->ct_dbdp.type != type) + continue; + if (ctp->ct_dbdp.dbflags != LF_ISSET(DB_SERVER_DBFLAGS)) + continue; + if (db_ctp->ct_dbdp.setflags != 0 && + ctp->ct_dbdp.setflags != db_ctp->ct_dbdp.setflags) + continue; + if (name == NULL || ctp->ct_dbdp.db == NULL || + strcmp(name, ctp->ct_dbdp.db) != 0) + continue; + if (subdb != ctp->ct_dbdp.subdb && + (subdb == NULL || ctp->ct_dbdp.subdb == NULL || + strcmp(subdb, ctp->ct_dbdp.subdb) != 0)) + continue; + /* + * If we get here, then we match. + */ + ctp->ct_refcount++; + return (ctp); + } + + return (NULL); +} + +extern "C" ct_entry * +__dbsrv_shareenv(ct_entry *env_ctp, home_entry *home, u_int32_t flags) +{ + ct_entry *ctp; + + /* + * Check if we can share an env. Criteria for sharing are: + * Must be an env ctp, obviously. + * Must share the same home env. + * All flags must match. + */ + for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL; + ctp = LIST_NEXT(ctp, entries)) { + /* + * Skip ourselves. + */ + if (ctp == env_ctp) + continue; + if (ctp->ct_type != CT_ENV) + continue; + if (ctp->ct_envdp.home != home) + continue; + if (ctp->ct_envdp.envflags != flags) + continue; + if (ctp->ct_envdp.onflags != env_ctp->ct_envdp.onflags) + continue; + if (ctp->ct_envdp.offflags != env_ctp->ct_envdp.offflags) + continue; + /* + * If we get here, then we match. The only thing left to + * check is the timeout. Since the server timeout set by + * the client is a hint, for sharing we'll give them the + * benefit of the doubt and grant them the longer timeout. + */ + if (ctp->ct_timeout < env_ctp->ct_timeout) + ctp->ct_timeout = env_ctp->ct_timeout; + ctp->ct_refcount++; + return (ctp); + } + + return (NULL); +} + +extern "C" void +__dbsrv_active(ct_entry *ctp) +{ + time_t t; + ct_entry *envctp; + + if (ctp == NULL) + return; + if ((t = time(NULL)) == -1) + return; + *(ctp->ct_activep) = t; + if ((envctp = ctp->ct_envparent) == NULL) + return; + *(envctp->ct_activep) = t; + return; +} + +extern "C" int +__db_close_int(long id, u_int32_t flags) +{ + Db *dbp; + int ret; + ct_entry *ctp; + + ret = 0; + ctp = get_tableent(id); + if (ctp == NULL) + return (DB_NOSERVER_ID); + DB_ASSERT(ctp->ct_type == CT_DB); + if (__dbsrv_verbose && ctp->ct_refcount != 1) + printf("Deref'ing dbp id %ld, refcount %d\n", + id, ctp->ct_refcount); + if (--ctp->ct_refcount != 0) + return (ret); + dbp = ctp->ct_dbp; + if (__dbsrv_verbose) + printf("Closing dbp id %ld\n", id); + + ret = dbp->close(flags); + __dbdel_ctp(ctp); + return (ret); +} + +extern "C" int +__dbc_close_int(ct_entry *dbc_ctp) +{ + Dbc *dbc; + int ret; + ct_entry *ctp; + + dbc = (Dbc *)dbc_ctp->ct_anyp; + + ret = dbc->close(); + /* + * If this cursor is a join cursor then we need to fix up the + * cursors that it was joined from so that they are independent again. + */ + if (dbc_ctp->ct_type & CT_JOINCUR) + for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL; + ctp = LIST_NEXT(ctp, entries)) { + /* + * Test if it is a join cursor, and if it is part + * of this one. + */ + if ((ctp->ct_type & CT_JOIN) && + ctp->ct_activep == &dbc_ctp->ct_active) { + ctp->ct_type &= ~CT_JOIN; + ctp->ct_activep = ctp->ct_origp; + __dbsrv_active(ctp); + } + } + __dbclear_ctp(dbc_ctp); + return (ret); + +} + +extern "C" int +__dbenv_close_int(long id, u_int32_t flags, int force) +{ + DbEnv *dbenv; + int ret; + ct_entry *ctp; + + ret = 0; + ctp = get_tableent(id); + if (ctp == NULL) + return (DB_NOSERVER_ID); + DB_ASSERT(ctp->ct_type == CT_ENV); + if (__dbsrv_verbose && ctp->ct_refcount != 1) + printf("Deref'ing env id %ld, refcount %d\n", + id, ctp->ct_refcount); + /* + * If we are timing out, we need to force the close, no matter + * what the refcount. + */ + if (--ctp->ct_refcount != 0 && !force) + return (ret); + dbenv = ctp->ct_envp; + if (__dbsrv_verbose) + printf("Closing env id %ld\n", id); + + ret = dbenv->close(flags); + __dbdel_ctp(ctp); + return (ret); +} + +static int +add_home(char *home) +{ + home_entry *hp, *homep; + int ret; + + if ((ret = __os_malloc(NULL, sizeof(home_entry), &hp)) != 0) + return (ret); + if ((ret = __os_malloc(NULL, strlen(home)+1, &hp->home)) != 0) + return (ret); + memcpy(hp->home, home, strlen(home)+1); + hp->dir = home; + hp->passwd = NULL; + /* + * This loop is to remove any trailing path separators, + * to assure hp->name points to the last component. + */ + hp->name = __db_rpath(home); + *(hp->name) = '\0'; + hp->name++; + while (*(hp->name) == '\0') { + hp->name = __db_rpath(home); + *(hp->name) = '\0'; + hp->name++; + } + /* + * Now we have successfully added it. Make sure there are no + * identical names. + */ + for (homep = LIST_FIRST(&__dbsrv_home); homep != NULL; + homep = LIST_NEXT(homep, entries)) + if (strcmp(homep->name, hp->name) == 0) { + printf("Already added home name %s, at directory %s\n", + hp->name, homep->dir); + return (-1); + } + LIST_INSERT_HEAD(&__dbsrv_home, hp, entries); + if (__dbsrv_verbose) + printf("Added home %s in dir %s\n", hp->name, hp->dir); + return (0); +} + +static int +add_passwd(char *passwd) +{ + home_entry *hp; + + /* + * We add the passwd to the last given home dir. If there + * isn't a home dir, or the most recent one already has a + * passwd, then there is a user error. + */ + hp = LIST_FIRST(&__dbsrv_home); + if (hp == NULL || hp->passwd != NULL) + return (EINVAL); + /* + * We've already strdup'ed the passwd above, so we don't need + * to malloc new space, just point to it. + */ + hp->passwd = passwd; + return (0); +} + +extern "C" home_entry * +get_home(char *name) +{ + home_entry *hp; + + for (hp = LIST_FIRST(&__dbsrv_home); hp != NULL; + hp = LIST_NEXT(hp, entries)) + if (strcmp(name, hp->name) == 0) + return (hp); + return (NULL); +} + +static int +env_recover(char *progname) +{ + DbEnv *dbenv; + home_entry *hp; + u_int32_t flags; + int exitval, ret; + + for (hp = LIST_FIRST(&__dbsrv_home); hp != NULL; + hp = LIST_NEXT(hp, entries)) { + exitval = 0; + dbenv = new DbEnv(DB_CXX_NO_EXCEPTIONS); + if (__dbsrv_verbose == 1) { + (void)dbenv->set_verbose(DB_VERB_RECOVERY, 1); + (void)dbenv->set_verbose(DB_VERB_CHKPOINT, 1); + } + dbenv->set_errfile(stderr); + dbenv->set_errpfx(progname); + if (hp->passwd != NULL) + (void)dbenv->set_encrypt(hp->passwd, DB_ENCRYPT_AES); + + /* + * Initialize the env with DB_RECOVER. That is all we + * have to do to run recovery. + */ + if (__dbsrv_verbose) + printf("Running recovery on %s\n", hp->home); + flags = DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | + DB_INIT_TXN | DB_USE_ENVIRON | DB_RECOVER; + if ((ret = dbenv->open(hp->home, flags, 0)) != 0) { + dbenv->err(ret, "DbEnv->open"); + goto error; + } + + if (0) { +error: exitval = 1; + } + if ((ret = dbenv->close(0)) != 0) { + exitval = 1; + fprintf(stderr, "%s: dbenv->close: %s\n", + progname, db_strerror(ret)); + } + if (exitval) + return (exitval); + } + return (0); +} |