Main Page | Class Hierarchy | Data Structures | Directories | File List | Data Fields | Related Pages

client.c

00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 1996-2005
00005  *      Sleepycat Software.  All rights reserved.
00006  *
00007  * $Id: client.c,v 12.2 2005/07/21 18:21:29 bostic Exp $
00008  */
00009 
00010 #include "db_config.h"
00011 
00012 #ifndef NO_SYSTEM_INCLUDES
00013 #include <sys/types.h>
00014 
00015 #ifdef HAVE_VXWORKS
00016 #include <rpcLib.h>
00017 #endif
00018 #include <rpc/rpc.h>
00019 
00020 #include <ctype.h>
00021 #include <stdlib.h>
00022 #include <string.h>
00023 #endif
00024 
00025 #include "db_server.h"
00026 
00027 #include "db_int.h"
00028 #include "dbinc/db_page.h"
00029 #include "dbinc/db_am.h"
00030 #include "dbinc/txn.h"
00031 #include "dbinc_auto/rpc_client_ext.h"
00032 
00033 static int __dbcl_c_destroy __P((DBC *));
00034 static int __dbcl_txn_close __P((DB_ENV *));
00035 
00036 /*
00037  * __dbcl_env_set_rpc_server --
00038  *      Initialize an environment's server.
00039  *
00040  * PUBLIC: int __dbcl_env_set_rpc_server
00041  * PUBLIC:     __P((DB_ENV *, void *, const char *, long, long, u_int32_t));
00042  */
00043 int
00044 __dbcl_env_set_rpc_server(dbenv, clnt, host, tsec, ssec, flags)
00045         DB_ENV *dbenv;
00046         void *clnt;
00047         const char *host;
00048         long tsec, ssec;
00049         u_int32_t flags;
00050 {
00051         CLIENT *cl;
00052         struct timeval tp;
00053 
00054         COMPQUIET(flags, 0);
00055 
00056 #ifdef HAVE_VXWORKS
00057         if (rpcTaskInit() != 0) {
00058                 __db_err(dbenv, "Could not initialize VxWorks RPC");
00059                 return (ERROR);
00060         }
00061 #endif
00062         if (RPC_ON(dbenv)) {
00063                 __db_err(dbenv, "Already set an RPC handle");
00064                 return (EINVAL);
00065         }
00066         /*
00067          * Only create the client and set its timeout if the user
00068          * did not pass us a client structure to begin with.
00069          */
00070         if (clnt == NULL) {
00071                 if ((cl = clnt_create((char *)host, DB_RPC_SERVERPROG,
00072                     DB_RPC_SERVERVERS, "tcp")) == NULL) {
00073                         __db_err(dbenv, clnt_spcreateerror((char *)host));
00074                         return (DB_NOSERVER);
00075                 }
00076                 if (tsec != 0) {
00077                         tp.tv_sec = tsec;
00078                         tp.tv_usec = 0;
00079                         (void)clnt_control(cl, CLSET_TIMEOUT, (char *)&tp);
00080                 }
00081         } else {
00082                 cl = (CLIENT *)clnt;
00083                 F_SET(dbenv, DB_ENV_RPCCLIENT_GIVEN);
00084         }
00085         dbenv->cl_handle = cl;
00086 
00087         return (__dbcl_env_create(dbenv, ssec));
00088 }
00089 
00090 /*
00091  * __dbcl_env_close_wrap --
00092  *      Wrapper function for DB_ENV->close function for clients.
00093  *      We need a wrapper function to deal with the case where we
00094  *      either don't call dbenv->open or close gets an error.
00095  *      We need to release the handle no matter what.
00096  *
00097  * PUBLIC: int __dbcl_env_close_wrap __P((DB_ENV *, u_int32_t));
00098  */
00099 int
00100 __dbcl_env_close_wrap(dbenv, flags)
00101         DB_ENV * dbenv;
00102         u_int32_t flags;
00103 {
00104         int ret, t_ret;
00105 
00106         ret = __dbcl_env_close(dbenv, flags);
00107         t_ret = __dbcl_refresh(dbenv);
00108         if (ret == 0 && t_ret != 0)
00109                 ret = t_ret;
00110         return (ret);
00111 }
00112 
00113 /*
00114  * __dbcl_env_open_wrap --
00115  *      Wrapper function for DB_ENV->open function for clients.
00116  *      We need a wrapper function to deal with DB_USE_ENVIRON* flags
00117  *      and we don't want to complicate the generated code for env_open.
00118  *
00119  * PUBLIC: int __dbcl_env_open_wrap
00120  * PUBLIC:     __P((DB_ENV *, const char *, u_int32_t, int));
00121  */
00122 int
00123 __dbcl_env_open_wrap(dbenv, home, flags, mode)
00124         DB_ENV * dbenv;
00125         const char * home;
00126         u_int32_t flags;
00127         int mode;
00128 {
00129         int ret;
00130 
00131         if (LF_ISSET(DB_THREAD)) {
00132                 __db_err(dbenv, "DB_THREAD not allowed on RPC clients");
00133                 return (EINVAL);
00134         }
00135         if ((ret = __db_home(dbenv, home, flags)) != 0)
00136                 return (ret);
00137         return (__dbcl_env_open(dbenv, dbenv->db_home, flags, mode));
00138 }
00139 
00140 /*
00141  * __dbcl_db_open_wrap --
00142  *      Wrapper function for DB->open function for clients.
00143  *      We need a wrapper function to error on DB_THREAD flag.
00144  *      and we don't want to complicate the generated code.
00145  *
00146  * PUBLIC: int __dbcl_db_open_wrap
00147  * PUBLIC:     __P((DB *, DB_TXN *, const char *, const char *,
00148  * PUBLIC:     DBTYPE, u_int32_t, int));
00149  */
00150 int
00151 __dbcl_db_open_wrap(dbp, txnp, name, subdb, type, flags, mode)
00152         DB * dbp;
00153         DB_TXN * txnp;
00154         const char * name;
00155         const char * subdb;
00156         DBTYPE type;
00157         u_int32_t flags;
00158         int mode;
00159 {
00160         return (__dbcl_db_open(dbp, txnp, name, subdb, type, flags, mode));
00161 }
00162 
00163 /*
00164  * __dbcl_refresh --
00165  *      Clean up an environment.
00166  *
00167  * PUBLIC: int __dbcl_refresh __P((DB_ENV *));
00168  */
00169 int
00170 __dbcl_refresh(dbenv)
00171         DB_ENV *dbenv;
00172 {
00173         CLIENT *cl;
00174         int ret;
00175 
00176         cl = (CLIENT *)dbenv->cl_handle;
00177 
00178         ret = 0;
00179         if (dbenv->tx_handle != NULL) {
00180                 /*
00181                  * We only need to free up our stuff, the caller
00182                  * of this function will call the server who will
00183                  * do all the real work.
00184                  */
00185                 ret = __dbcl_txn_close(dbenv);
00186                 dbenv->tx_handle = NULL;
00187         }
00188         if (!F_ISSET(dbenv, DB_ENV_RPCCLIENT_GIVEN) && cl != NULL)
00189                 clnt_destroy(cl);
00190         dbenv->cl_handle = NULL;
00191         if (dbenv->db_home != NULL) {
00192                 __os_free(dbenv, dbenv->db_home);
00193                 dbenv->db_home = NULL;
00194         }
00195         return (ret);
00196 }
00197 
00198 /*
00199  * __dbcl_retcopy --
00200  *      Copy the returned data into the user's DBT, handling allocation flags,
00201  *      but not DB_DBT_PARTIAL.
00202  *
00203  * PUBLIC: int __dbcl_retcopy __P((DB_ENV *, DBT *,
00204  * PUBLIC:    void *, u_int32_t, void **, u_int32_t *));
00205  */
00206 int
00207 __dbcl_retcopy(dbenv, dbt, data, len, memp, memsize)
00208         DB_ENV *dbenv;
00209         DBT *dbt;
00210         void *data;
00211         u_int32_t len;
00212         void **memp;
00213         u_int32_t *memsize;
00214 {
00215         int ret;
00216         u_int32_t orig_flags;
00217 
00218         /*
00219          * The RPC server handles DB_DBT_PARTIAL, so we mask it out here to
00220          * avoid the handling of partials in __db_retcopy.  Check first whether
00221          * the data has actually changed, so we don't try to copy over
00222          * read-only keys, which the RPC server always returns regardless.
00223          */
00224         orig_flags = dbt->flags;
00225         F_CLR(dbt, DB_DBT_PARTIAL);
00226         if (dbt->data != NULL && dbt->size == len &&
00227             memcmp(dbt->data, data, len) == 0)
00228                 ret = 0;
00229         else
00230                 ret = __db_retcopy(dbenv, dbt, data, len, memp, memsize);
00231         dbt->flags = orig_flags;
00232         return (ret);
00233 }
00234 
00235 /*
00236  * __dbcl_txn_close --
00237  *      Clean up an environment's transactions.
00238  */
00239 static int
00240 __dbcl_txn_close(dbenv)
00241         DB_ENV *dbenv;
00242 {
00243         DB_TXN *txnp;
00244         DB_TXNMGR *tmgrp;
00245         int ret;
00246 
00247         ret = 0;
00248         tmgrp = dbenv->tx_handle;
00249 
00250         /*
00251          * This function can only be called once per process (i.e., not
00252          * once per thread), so no synchronization is required.
00253          * Also this function is called *after* the server has been called,
00254          * so the server has already closed/aborted any transactions that
00255          * were open on its side.  We only need to do local cleanup.
00256          */
00257         while ((txnp = TAILQ_FIRST(&tmgrp->txn_chain)) != NULL)
00258                 __dbcl_txn_end(txnp);
00259 
00260         __os_free(dbenv, tmgrp);
00261         return (ret);
00262 
00263 }
00264 
00265 /*
00266  * __dbcl_txn_end --
00267  *      Clean up an transaction.
00268  * RECURSIVE FUNCTION:  Clean up nested transactions.
00269  *
00270  * PUBLIC: void __dbcl_txn_end __P((DB_TXN *));
00271  */
00272 void
00273 __dbcl_txn_end(txnp)
00274         DB_TXN *txnp;
00275 {
00276         DB_ENV *dbenv;
00277         DB_TXN *kids;
00278         DB_TXNMGR *mgr;
00279 
00280         mgr = txnp->mgrp;
00281         dbenv = mgr->dbenv;
00282 
00283         /*
00284          * First take care of any kids we have
00285          */
00286         for (kids = TAILQ_FIRST(&txnp->kids);
00287             kids != NULL;
00288             kids = TAILQ_FIRST(&txnp->kids))
00289                 __dbcl_txn_end(kids);
00290 
00291         /*
00292          * We are ending this transaction no matter what the parent
00293          * may eventually do, if we have a parent.  All those details
00294          * are taken care of by the server.  We only need to make sure
00295          * that we properly release resources.
00296          */
00297         if (txnp->parent != NULL)
00298                 TAILQ_REMOVE(&txnp->parent->kids, txnp, klinks);
00299         TAILQ_REMOVE(&mgr->txn_chain, txnp, links);
00300         __os_free(dbenv, txnp);
00301 }
00302 
00303 /*
00304  * __dbcl_txn_setup --
00305  *      Setup a client transaction structure.
00306  *
00307  * PUBLIC: void __dbcl_txn_setup __P((DB_ENV *, DB_TXN *, DB_TXN *, u_int32_t));
00308  */
00309 void
00310 __dbcl_txn_setup(dbenv, txn, parent, id)
00311         DB_ENV *dbenv;
00312         DB_TXN *txn;
00313         DB_TXN *parent;
00314         u_int32_t id;
00315 {
00316         txn->mgrp = dbenv->tx_handle;
00317         txn->parent = parent;
00318         txn->txnid = id;
00319 
00320         /*
00321          * XXX
00322          * In DB library the txn_chain is protected by the mgrp->mutexp.
00323          * However, that mutex is implemented in the environments shared
00324          * memory region.  The client library does not support all of the
00325          * region - that just get forwarded to the server.  Therefore,
00326          * the chain is unprotected here, but properly protected on the
00327          * server.
00328          */
00329         TAILQ_INSERT_TAIL(&txn->mgrp->txn_chain, txn, links);
00330 
00331         TAILQ_INIT(&txn->kids);
00332 
00333         if (parent != NULL)
00334                 TAILQ_INSERT_HEAD(&parent->kids, txn, klinks);
00335 
00336         __dbcl_txn_init(txn);
00337 
00338         txn->flags = TXN_MALLOC;
00339 }
00340 
00341 /*
00342  * __dbcl_c_destroy --
00343  *      Destroy a cursor.
00344  */
00345 static int
00346 __dbcl_c_destroy(dbc)
00347         DBC *dbc;
00348 {
00349         DB *dbp;
00350 
00351         dbp = dbc->dbp;
00352 
00353         TAILQ_REMOVE(&dbp->free_queue, dbc, links);
00354         /* Discard any memory used to store returned data. */
00355         if (dbc->my_rskey.data != NULL)
00356                 __os_free(dbc->dbp->dbenv, dbc->my_rskey.data);
00357         if (dbc->my_rkey.data != NULL)
00358                 __os_free(dbc->dbp->dbenv, dbc->my_rkey.data);
00359         if (dbc->my_rdata.data != NULL)
00360                 __os_free(dbc->dbp->dbenv, dbc->my_rdata.data);
00361         __os_free(NULL, dbc);
00362 
00363         return (0);
00364 }
00365 
00366 /*
00367  * __dbcl_c_refresh --
00368  *      Refresh a cursor.  Move it from the active queue to the free queue.
00369  *
00370  * PUBLIC: void __dbcl_c_refresh __P((DBC *));
00371  */
00372 void
00373 __dbcl_c_refresh(dbc)
00374         DBC *dbc;
00375 {
00376         DB *dbp;
00377 
00378         dbp = dbc->dbp;
00379         dbc->flags = 0;
00380         dbc->cl_id = 0;
00381 
00382         /*
00383          * If dbp->cursor fails locally, we use a local dbc so that
00384          * we can close it.  In that case, dbp will be NULL.
00385          */
00386         if (dbp != NULL) {
00387                 TAILQ_REMOVE(&dbp->active_queue, dbc, links);
00388                 TAILQ_INSERT_TAIL(&dbp->free_queue, dbc, links);
00389         }
00390 }
00391 
00392 /*
00393  * __dbcl_c_setup --
00394  *      Allocate a cursor.
00395  *
00396  * PUBLIC: int __dbcl_c_setup __P((u_int, DB *, DBC **));
00397  */
00398 int
00399 __dbcl_c_setup(cl_id, dbp, dbcp)
00400         u_int cl_id;
00401         DB *dbp;
00402         DBC **dbcp;
00403 {
00404         DBC *dbc, tmpdbc;
00405         int ret;
00406 
00407         if ((dbc = TAILQ_FIRST(&dbp->free_queue)) != NULL)
00408                 TAILQ_REMOVE(&dbp->free_queue, dbc, links);
00409         else {
00410                 if ((ret =
00411                     __os_calloc(dbp->dbenv, 1, sizeof(DBC), &dbc)) != 0) {
00412                         /*
00413                          * If we die here, set up a tmp dbc to call the
00414                          * server to shut down that cursor.
00415                          */
00416                         tmpdbc.dbp = NULL;
00417                         tmpdbc.cl_id = cl_id;
00418                         (void)__dbcl_dbc_c_close(&tmpdbc);
00419                         return (ret);
00420                 }
00421 
00422                 __dbcl_dbc_init(dbc);
00423 
00424                 /*
00425                  * !!!
00426                  * Set up the local destroy function -- we're not really
00427                  * an access method, but it does what we need.
00428                  */
00429                 dbc->c_am_destroy = __dbcl_c_destroy;
00430         }
00431         dbc->cl_id = cl_id;
00432         dbc->dbp = dbp;
00433         TAILQ_INSERT_TAIL(&dbp->active_queue, dbc, links);
00434         *dbcp = dbc;
00435         return (0);
00436 }
00437 
00438 /*
00439  * __dbcl_dbclose_common --
00440  *      Common code for closing/cleaning a dbp.
00441  *
00442  * PUBLIC: int __dbcl_dbclose_common __P((DB *));
00443  */
00444 int
00445 __dbcl_dbclose_common(dbp)
00446         DB *dbp;
00447 {
00448         int ret, t_ret;
00449         DBC *dbc;
00450 
00451         /*
00452          * Go through the active cursors and call the cursor recycle routine,
00453          * which resolves pending operations and moves the cursors onto the
00454          * free list.  Then, walk the free list and call the cursor destroy
00455          * routine.
00456          *
00457          * NOTE:  We do not need to use the join_queue for join cursors.
00458          * See comment in __dbcl_dbjoin_ret.
00459          */
00460         ret = 0;
00461         while ((dbc = TAILQ_FIRST(&dbp->active_queue)) != NULL)
00462                 __dbcl_c_refresh(dbc);
00463         while ((dbc = TAILQ_FIRST(&dbp->free_queue)) != NULL)
00464                 if ((t_ret = __dbcl_c_destroy(dbc)) != 0 && ret == 0)
00465                         ret = t_ret;
00466 
00467         TAILQ_INIT(&dbp->free_queue);
00468         TAILQ_INIT(&dbp->active_queue);
00469         /* Discard any memory used to store returned data. */
00470         if (dbp->my_rskey.data != NULL)
00471                 __os_free(dbp->dbenv, dbp->my_rskey.data);
00472         if (dbp->my_rkey.data != NULL)
00473                 __os_free(dbp->dbenv, dbp->my_rkey.data);
00474         if (dbp->my_rdata.data != NULL)
00475                 __os_free(dbp->dbenv, dbp->my_rdata.data);
00476 
00477         memset(dbp, CLEAR_BYTE, sizeof(*dbp));
00478         __os_free(NULL, dbp);
00479         return (ret);
00480 }

Generated on Sun Dec 25 12:14:45 2005 for Berkeley DB 4.4.16 by  doxygen 1.4.2