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

db_server_util.c

00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 2000-2005
00005  *      Sleepycat Software.  All rights reserved.
00006  *
00007  * $Id: db_server_util.c,v 12.3 2005/06/16 20:23:39 bostic Exp $
00008  */
00009 
00010 #include "db_config.h"
00011 
00012 #ifndef NO_SYSTEM_INCLUDES
00013 #include <sys/types.h>
00014 
00015 #if TIME_WITH_SYS_TIME
00016 #include <sys/time.h>
00017 #include <time.h>
00018 #else
00019 #if HAVE_SYS_TIME_H
00020 #include <sys/time.h>
00021 #else
00022 #include <time.h>
00023 #endif
00024 #endif
00025 
00026 #include <rpc/rpc.h>
00027 
00028 #include <limits.h>
00029 #include <signal.h>
00030 #include <stdio.h>
00031 #include <stdlib.h>
00032 #include <string.h>
00033 #include <unistd.h>
00034 #endif
00035 
00036 #include "db_server.h"
00037 
00038 #include "db_int.h"
00039 #include "dbinc_auto/clib_ext.h"
00040 #include "dbinc/db_server_int.h"
00041 #include "dbinc_auto/common_ext.h"
00042 #include "dbinc_auto/rpc_server_ext.h"
00043 
00044 static int add_home __P((char *));
00045 static int add_passwd __P((char *));
00046 static int env_recover __P((char *));
00047 static void __dbclear_child __P((ct_entry *));
00048 
00049 static LIST_HEAD(cthead, ct_entry) __dbsrv_head;
00050 static LIST_HEAD(homehead, home_entry) __dbsrv_home;
00051 static long __dbsrv_defto = DB_SERVER_TIMEOUT;
00052 static long __dbsrv_maxto = DB_SERVER_MAXTIMEOUT;
00053 static long __dbsrv_idleto = DB_SERVER_IDLETIMEOUT;
00054 static char *logfile = NULL;
00055 static char *prog;
00056 
00057 static void usage __P((void));
00058 static void version_check __P((void));
00059 
00060 int __dbsrv_verbose = 0;
00061 
00062 int
00063 main(argc, argv)
00064         int argc;
00065         char **argv;
00066 {
00067         extern int __dbsrv_main();
00068         extern char *optarg;
00069         CLIENT *cl;
00070         int ch, ret;
00071         char *passwd;
00072 
00073         prog = argv[0];
00074 
00075         version_check();
00076 
00077         ret = 0;
00078         /*
00079          * Check whether another server is running or not.  There
00080          * is a race condition where two servers could be racing to
00081          * register with the portmapper.  The goal of this check is to
00082          * forbid running additional servers (like those started from
00083          * the test suite) if the user is already running one.
00084          *
00085          * XXX
00086          * This does not solve nor prevent two servers from being
00087          * started at the same time and running recovery at the same
00088          * time on the same environments.
00089          */
00090         if ((cl = clnt_create("localhost",
00091             DB_RPC_SERVERPROG, DB_RPC_SERVERVERS, "tcp")) != NULL) {
00092                 fprintf(stderr,
00093                     "%s: Berkeley DB RPC server already running.\n", prog);
00094                 clnt_destroy(cl);
00095                 return (EXIT_FAILURE);
00096         }
00097 
00098         LIST_INIT(&__dbsrv_home);
00099         while ((ch = getopt(argc, argv, "h:I:L:P:t:T:Vv")) != EOF)
00100                 switch (ch) {
00101                 case 'h':
00102                         (void)add_home(optarg);
00103                         break;
00104                 case 'I':
00105                         if (__db_getlong(NULL, prog,
00106                             optarg, 1, LONG_MAX, &__dbsrv_idleto))
00107                                 return (EXIT_FAILURE);
00108                         break;
00109                 case 'L':
00110                         logfile = optarg;
00111                         break;
00112                 case 'P':
00113                         passwd = strdup(optarg);
00114                         memset(optarg, 0, strlen(optarg));
00115                         if (passwd == NULL) {
00116                                 fprintf(stderr, "%s: strdup: %s\n",
00117                                     prog, strerror(errno));
00118                                 return (EXIT_FAILURE);
00119                         }
00120                         if ((ret = add_passwd(passwd)) != 0) {
00121                                 fprintf(stderr, "%s: strdup: %s\n",
00122                                     prog, strerror(ret));
00123                                 return (EXIT_FAILURE);
00124                         }
00125                         break;
00126                 case 't':
00127                         if (__db_getlong(NULL, prog,
00128                             optarg, 1, LONG_MAX, &__dbsrv_defto))
00129                                 return (EXIT_FAILURE);
00130                         break;
00131                 case 'T':
00132                         if (__db_getlong(NULL, prog,
00133                             optarg, 1, LONG_MAX, &__dbsrv_maxto))
00134                                 return (EXIT_FAILURE);
00135                         break;
00136                 case 'V':
00137                         printf("%s\n", db_version(NULL, NULL, NULL));
00138                         return (EXIT_SUCCESS);
00139                 case 'v':
00140                         __dbsrv_verbose = 1;
00141                         break;
00142                 default:
00143                         usage();
00144                 }
00145         /*
00146          * Check default timeout against maximum timeout
00147          */
00148         if (__dbsrv_defto > __dbsrv_maxto)
00149                 __dbsrv_defto = __dbsrv_maxto;
00150 
00151         /*
00152          * Check default timeout against idle timeout
00153          * It would be bad to timeout environments sooner than txns.
00154          */
00155         if (__dbsrv_defto > __dbsrv_idleto)
00156                 fprintf(stderr,
00157             "%s: WARNING: Idle timeout %ld is less than resource timeout %ld\n",
00158                     prog, __dbsrv_idleto, __dbsrv_defto);
00159 
00160         LIST_INIT(&__dbsrv_head);
00161 
00162         /*
00163          * If a client crashes during an RPC, our reply to it
00164          * generates a SIGPIPE.  Ignore SIGPIPE so we don't exit unnecessarily.
00165          */
00166 #ifdef SIGPIPE
00167         signal(SIGPIPE, SIG_IGN);
00168 #endif
00169 
00170         if (logfile != NULL && __db_util_logset("berkeley_db_svc", logfile))
00171                 return (EXIT_FAILURE);
00172 
00173         /*
00174          * Now that we are ready to start, run recovery on all the
00175          * environments specified.
00176          */
00177         if (env_recover(prog) != 0)
00178                 return (EXIT_FAILURE);
00179 
00180         /*
00181          * We've done our setup, now call the generated server loop
00182          */
00183         if (__dbsrv_verbose)
00184                 printf("%s:  Ready to receive requests\n", prog);
00185         __dbsrv_main();
00186 
00187         abort();
00188 
00189         /* NOTREACHED */
00190         return (0);
00191 }
00192 
00193 static void
00194 usage()
00195 {
00196         fprintf(stderr, "usage: %s %s\n\t%s\n", prog,
00197             "[-Vv] [-h home] [-P passwd]",
00198             "[-I idletimeout] [-L logfile] [-t def_timeout] [-T maxtimeout]");
00199         exit(EXIT_FAILURE);
00200 }
00201 
00202 static void
00203 version_check()
00204 {
00205         int v_major, v_minor, v_patch;
00206 
00207         /* Make sure we're loaded with the right version of the DB library. */
00208         (void)db_version(&v_major, &v_minor, &v_patch);
00209         if (v_major != DB_VERSION_MAJOR ||
00210             v_minor != DB_VERSION_MINOR || v_patch != DB_VERSION_PATCH) {
00211                 fprintf(stderr,
00212         "%s: version %d.%d.%d doesn't match library version %d.%d.%d\n",
00213                     prog, DB_VERSION_MAJOR, DB_VERSION_MINOR,
00214                     DB_VERSION_PATCH, v_major, v_minor, v_patch);
00215                 exit(EXIT_FAILURE);
00216         }
00217 }
00218 
00219 /*
00220  * PUBLIC: void __dbsrv_settimeout __P((ct_entry *, u_int32_t));
00221  */
00222 void
00223 __dbsrv_settimeout(ctp, to)
00224         ct_entry *ctp;
00225         u_int32_t to;
00226 {
00227         if (to > (u_int32_t)__dbsrv_maxto)
00228                 ctp->ct_timeout = __dbsrv_maxto;
00229         else if (to <= 0)
00230                 ctp->ct_timeout = __dbsrv_defto;
00231         else
00232                 ctp->ct_timeout = to;
00233 }
00234 
00235 /*
00236  * PUBLIC: void __dbsrv_timeout __P((int));
00237  */
00238 void
00239 __dbsrv_timeout(force)
00240         int force;
00241 {
00242         static long to_hint = -1;
00243         time_t t;
00244         long to;
00245         ct_entry *ctp, *nextctp;
00246 
00247         if ((t = time(NULL)) == -1)
00248                 return;
00249 
00250         /*
00251          * Check hint.  If hint is further in the future
00252          * than now, no work to do.
00253          */
00254         if (!force && to_hint > 0 && t < to_hint)
00255                 return;
00256         to_hint = -1;
00257         /*
00258          * Timeout transactions or cursors holding DB resources.
00259          * Do this before timing out envs to properly release resources.
00260          *
00261          * !!!
00262          * We can just loop through this list looking for cursors and txns.
00263          * We do not need to verify txn and cursor relationships at this
00264          * point because we maintain the list in LIFO order *and* we
00265          * maintain activity in the ultimate txn parent of any cursor
00266          * so either everything in a txn is timing out, or nothing.
00267          * So, since we are LIFO, we will correctly close/abort all the
00268          * appropriate handles, in the correct order.
00269          */
00270         for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL; ctp = nextctp) {
00271                 nextctp = LIST_NEXT(ctp, entries);
00272                 switch (ctp->ct_type) {
00273                 case CT_TXN:
00274                         to = *(ctp->ct_activep) + ctp->ct_timeout;
00275                         /* TIMEOUT */
00276                         if (to < t) {
00277                                 if (__dbsrv_verbose)
00278                                         printf("Timing out txn id %ld\n",
00279                                             ctp->ct_id);
00280                                 (void)((DB_TXN *)ctp->ct_anyp)->
00281                                     abort((DB_TXN *)ctp->ct_anyp);
00282                                 __dbdel_ctp(ctp);
00283                                 /*
00284                                  * If we timed out an txn, we may have closed
00285                                  * all sorts of ctp's.
00286                                  * So start over with a guaranteed good ctp.
00287                                  */
00288                                 nextctp = LIST_FIRST(&__dbsrv_head);
00289                         } else if ((to_hint > 0 && to_hint > to) ||
00290                             to_hint == -1)
00291                                 to_hint = to;
00292                         break;
00293                 case CT_CURSOR:
00294                 case (CT_JOINCUR | CT_CURSOR):
00295                         to = *(ctp->ct_activep) + ctp->ct_timeout;
00296                         /* TIMEOUT */
00297                         if (to < t) {
00298                                 if (__dbsrv_verbose)
00299                                         printf("Timing out cursor %ld\n",
00300                                             ctp->ct_id);
00301                                 (void)__dbc_close_int(ctp);
00302                                 /*
00303                                  * Start over with a guaranteed good ctp.
00304                                  */
00305                                 nextctp = LIST_FIRST(&__dbsrv_head);
00306                         } else if ((to_hint > 0 && to_hint > to) ||
00307                             to_hint == -1)
00308                                 to_hint = to;
00309                         break;
00310                 default:
00311                         break;
00312                 }
00313         }
00314         /*
00315          * Timeout idle handles.
00316          * If we are forcing a timeout, we'll close all env handles.
00317          */
00318         for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL; ctp = nextctp) {
00319                 nextctp = LIST_NEXT(ctp, entries);
00320                 if (ctp->ct_type != CT_ENV)
00321                         continue;
00322                 to = *(ctp->ct_activep) + ctp->ct_idle;
00323                 /* TIMEOUT */
00324                 if (to < t || force) {
00325                         if (__dbsrv_verbose)
00326                                 printf("Timing out env id %ld\n", ctp->ct_id);
00327                         (void)__env_close_int(ctp->ct_id, 0, 1);
00328                         /*
00329                          * If we timed out an env, we may have closed
00330                          * all sorts of ctp's (maybe even all of them.
00331                          * So start over with a guaranteed good ctp.
00332                          */
00333                         nextctp = LIST_FIRST(&__dbsrv_head);
00334                 }
00335         }
00336 }
00337 
00338 /*
00339  * RECURSIVE FUNCTION.  We need to clear/free any number of levels of nested
00340  * layers.
00341  */
00342 static void
00343 __dbclear_child(parent)
00344         ct_entry *parent;
00345 {
00346         ct_entry *ctp, *nextctp;
00347 
00348         for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL;
00349             ctp = nextctp) {
00350                 nextctp = LIST_NEXT(ctp, entries);
00351                 if (ctp->ct_type == 0)
00352                         continue;
00353                 if (ctp->ct_parent == parent) {
00354                         __dbclear_child(ctp);
00355                         /*
00356                          * Need to do this here because le_next may
00357                          * have changed with the recursive call and we
00358                          * don't want to point to a removed entry.
00359                          */
00360                         nextctp = LIST_NEXT(ctp, entries);
00361                         __dbclear_ctp(ctp);
00362                 }
00363         }
00364 }
00365 
00366 /*
00367  * PUBLIC: void __dbclear_ctp __P((ct_entry *));
00368  */
00369 void
00370 __dbclear_ctp(ctp)
00371         ct_entry *ctp;
00372 {
00373         LIST_REMOVE(ctp, entries);
00374         __os_free(NULL, ctp);
00375 }
00376 
00377 /*
00378  * PUBLIC: void __dbdel_ctp __P((ct_entry *));
00379  */
00380 void
00381 __dbdel_ctp(parent)
00382         ct_entry *parent;
00383 {
00384         __dbclear_child(parent);
00385         __dbclear_ctp(parent);
00386 }
00387 
00388 /*
00389  * PUBLIC: ct_entry *new_ct_ent __P((int *));
00390  */
00391 ct_entry *
00392 new_ct_ent(errp)
00393         int *errp;
00394 {
00395         time_t t;
00396         ct_entry *ctp, *octp;
00397         int ret;
00398 
00399         if ((ret = __os_malloc(NULL, sizeof(ct_entry), &ctp)) != 0) {
00400                 *errp = ret;
00401                 return (NULL);
00402         }
00403         memset(ctp, 0, sizeof(ct_entry));
00404         /*
00405          * Get the time as ID.  We may service more than one request per
00406          * second however.  If we are, then increment id value until we
00407          * find an unused one.  We insert entries in LRU fashion at the
00408          * head of the list.  So, if the first entry doesn't match, then
00409          * we know for certain that we can use our entry.
00410          */
00411         if ((t = time(NULL)) == -1) {
00412                 *errp = __os_get_errno();
00413                 __os_free(NULL, ctp);
00414                 return (NULL);
00415         }
00416         octp = LIST_FIRST(&__dbsrv_head);
00417         if (octp != NULL && octp->ct_id >= t)
00418                 t = octp->ct_id + 1;
00419         ctp->ct_id = (long)t;
00420         ctp->ct_idle = __dbsrv_idleto;
00421         ctp->ct_activep = &ctp->ct_active;
00422         ctp->ct_origp = NULL;
00423         ctp->ct_refcount = 1;
00424 
00425         LIST_INSERT_HEAD(&__dbsrv_head, ctp, entries);
00426         return (ctp);
00427 }
00428 
00429 /*
00430  * PUBLIC: ct_entry *get_tableent __P((long));
00431  */
00432 ct_entry *
00433 get_tableent(id)
00434         long id;
00435 {
00436         ct_entry *ctp;
00437 
00438         for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL;
00439             ctp = LIST_NEXT(ctp, entries))
00440                 if (ctp->ct_id == id)
00441                         return (ctp);
00442         return (NULL);
00443 }
00444 
00445 /*
00446  * PUBLIC: ct_entry *__dbsrv_sharedb __P((ct_entry *, const char *,
00447  * PUBLIC:    const char *, DBTYPE, u_int32_t));
00448  */
00449 ct_entry *
00450 __dbsrv_sharedb(db_ctp, name, subdb, type, flags)
00451         ct_entry *db_ctp;
00452         const char *name, *subdb;
00453         DBTYPE type;
00454         u_int32_t flags;
00455 {
00456         ct_entry *ctp;
00457 
00458         /*
00459          * Check if we can share a db handle.  Criteria for sharing are:
00460          * If any of the non-sharable flags are set, we cannot share.
00461          * Must be a db ctp, obviously.
00462          * Must share the same env parent.
00463          * Must be the same type, or current one DB_UNKNOWN.
00464          * Must be same byteorder, or current one must not care.
00465          * All flags must match.
00466          * Must be same name, but don't share in-memory databases.
00467          * Must be same subdb name.
00468          */
00469         if (flags & DB_SERVER_DBNOSHARE)
00470                 return (NULL);
00471         for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL;
00472             ctp = LIST_NEXT(ctp, entries)) {
00473                 /*
00474                  * Skip ourselves.
00475                  */
00476                 if (ctp == db_ctp)
00477                         continue;
00478                 if (ctp->ct_type != CT_DB)
00479                         continue;
00480                 if (ctp->ct_envparent != db_ctp->ct_envparent)
00481                         continue;
00482                 if (type != DB_UNKNOWN && ctp->ct_dbdp.type != type)
00483                         continue;
00484                 if (ctp->ct_dbdp.dbflags != LF_ISSET(DB_SERVER_DBFLAGS))
00485                         continue;
00486                 if (db_ctp->ct_dbdp.setflags != 0 &&
00487                     ctp->ct_dbdp.setflags != db_ctp->ct_dbdp.setflags)
00488                         continue;
00489                 if (name == NULL || ctp->ct_dbdp.db == NULL ||
00490                     strcmp(name, ctp->ct_dbdp.db) != 0)
00491                         continue;
00492                 if (subdb != ctp->ct_dbdp.subdb &&
00493                     (subdb == NULL || ctp->ct_dbdp.subdb == NULL ||
00494                     strcmp(subdb, ctp->ct_dbdp.subdb) != 0))
00495                         continue;
00496                 /*
00497                  * If we get here, then we match.
00498                  */
00499                 ctp->ct_refcount++;
00500                 return (ctp);
00501         }
00502 
00503         return (NULL);
00504 }
00505 
00506 /*
00507  * PUBLIC: ct_entry *__dbsrv_shareenv
00508  * PUBLIC:     __P((ct_entry *, home_entry *, u_int32_t));
00509  */
00510 ct_entry *
00511 __dbsrv_shareenv(env_ctp, home, flags)
00512         ct_entry *env_ctp;
00513         home_entry *home;
00514         u_int32_t flags;
00515 {
00516         ct_entry *ctp;
00517 
00518         /*
00519          * Check if we can share an env.  Criteria for sharing are:
00520          * Must be an env ctp, obviously.
00521          * Must share the same home env.
00522          * All flags must match.
00523          */
00524         for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL;
00525             ctp = LIST_NEXT(ctp, entries)) {
00526                 /*
00527                  * Skip ourselves.
00528                  */
00529                 if (ctp == env_ctp)
00530                         continue;
00531                 if (ctp->ct_type != CT_ENV)
00532                         continue;
00533                 if (ctp->ct_envdp.home != home)
00534                         continue;
00535                 if (ctp->ct_envdp.envflags != flags)
00536                         continue;
00537                 if (ctp->ct_envdp.onflags != env_ctp->ct_envdp.onflags)
00538                         continue;
00539                 if (ctp->ct_envdp.offflags != env_ctp->ct_envdp.offflags)
00540                         continue;
00541                 /*
00542                  * If we get here, then we match.  The only thing left to
00543                  * check is the timeout.  Since the server timeout set by
00544                  * the client is a hint, for sharing we'll give them the
00545                  * benefit of the doubt and grant them the longer timeout.
00546                  */
00547                 if (ctp->ct_timeout < env_ctp->ct_timeout)
00548                         ctp->ct_timeout = env_ctp->ct_timeout;
00549                 ctp->ct_refcount++;
00550                 return (ctp);
00551         }
00552 
00553         return (NULL);
00554 }
00555 
00556 /*
00557  * PUBLIC: void __dbsrv_active __P((ct_entry *));
00558  */
00559 void
00560 __dbsrv_active(ctp)
00561         ct_entry *ctp;
00562 {
00563         time_t t;
00564         ct_entry *envctp;
00565 
00566         if (ctp == NULL)
00567                 return;
00568         if ((t = time(NULL)) == -1)
00569                 return;
00570         *(ctp->ct_activep) = t;
00571         if ((envctp = ctp->ct_envparent) == NULL)
00572                 return;
00573         *(envctp->ct_activep) = t;
00574         return;
00575 }
00576 
00577 /*
00578  * PUBLIC: int __db_close_int __P((long, u_int32_t));
00579  */
00580 int
00581 __db_close_int(id, flags)
00582         long id;
00583         u_int32_t flags;
00584 {
00585         DB *dbp;
00586         int ret;
00587         ct_entry *ctp;
00588 
00589         ret = 0;
00590         ctp = get_tableent(id);
00591         if (ctp == NULL)
00592                 return (DB_NOSERVER_ID);
00593         DB_ASSERT(ctp->ct_type == CT_DB);
00594         if (__dbsrv_verbose && ctp->ct_refcount != 1)
00595                 printf("Deref'ing dbp id %ld, refcount %d\n",
00596                     id, ctp->ct_refcount);
00597         if (--ctp->ct_refcount != 0)
00598                 return (ret);
00599         dbp = ctp->ct_dbp;
00600         if (__dbsrv_verbose)
00601                 printf("Closing dbp id %ld\n", id);
00602 
00603         ret = dbp->close(dbp, flags);
00604         if (ctp->ct_dbdp.db != NULL)
00605                 __os_free(NULL, ctp->ct_dbdp.db);
00606         if (ctp->ct_dbdp.subdb != NULL)
00607                 __os_free(NULL, ctp->ct_dbdp.subdb);
00608         __dbdel_ctp(ctp);
00609         return (ret);
00610 }
00611 
00612 /*
00613  * PUBLIC: int __dbc_close_int __P((ct_entry *));
00614  */
00615 int
00616 __dbc_close_int(dbc_ctp)
00617         ct_entry *dbc_ctp;
00618 {
00619         DBC *dbc;
00620         int ret;
00621         ct_entry *ctp;
00622 
00623         dbc = (DBC *)dbc_ctp->ct_anyp;
00624 
00625         ret = dbc->c_close(dbc);
00626         /*
00627          * If this cursor is a join cursor then we need to fix up the
00628          * cursors that it was joined from so that they are independent again.
00629          */
00630         if (dbc_ctp->ct_type & CT_JOINCUR)
00631                 for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL;
00632                     ctp = LIST_NEXT(ctp, entries)) {
00633                         /*
00634                          * Test if it is a join cursor, and if it is part
00635                          * of this one.
00636                          */
00637                         if ((ctp->ct_type & CT_JOIN) &&
00638                             ctp->ct_activep == &dbc_ctp->ct_active) {
00639                                 ctp->ct_type &= ~CT_JOIN;
00640                                 ctp->ct_activep = ctp->ct_origp;
00641                                 __dbsrv_active(ctp);
00642                         }
00643                 }
00644         __dbclear_ctp(dbc_ctp);
00645         return (ret);
00646 
00647 }
00648 
00649 /*
00650  * PUBLIC: int __env_close_int __P((long, u_int32_t, int));
00651  */
00652 int
00653 __env_close_int(id, flags, force)
00654         long id;
00655         u_int32_t flags;
00656         int force;
00657 {
00658         DB_ENV *dbenv;
00659         int ret;
00660         ct_entry *ctp, *dbctp, *nextctp;
00661 
00662         ret = 0;
00663         ctp = get_tableent(id);
00664         if (ctp == NULL)
00665                 return (DB_NOSERVER_ID);
00666         DB_ASSERT(ctp->ct_type == CT_ENV);
00667         if (__dbsrv_verbose && ctp->ct_refcount != 1)
00668                 printf("Deref'ing env id %ld, refcount %d\n",
00669                     id, ctp->ct_refcount);
00670         /*
00671          * If we are timing out, we need to force the close, no matter
00672          * what the refcount.
00673          */
00674         if (--ctp->ct_refcount != 0 && !force)
00675                 return (ret);
00676         dbenv = ctp->ct_envp;
00677         if (__dbsrv_verbose)
00678                 printf("Closing env id %ld\n", id);
00679 
00680         /*
00681          * If we're timing out an env, we want to close all of its
00682          * database handles as well.  All of the txns and cursors
00683          * must have been timed out prior to timing out the env.
00684          */
00685         if (force)
00686                 for (dbctp = LIST_FIRST(&__dbsrv_head);
00687                     dbctp != NULL; dbctp = nextctp) {
00688                         nextctp = LIST_NEXT(dbctp, entries);
00689                         if (dbctp->ct_type != CT_DB)
00690                                 continue;
00691                         if (dbctp->ct_envparent != ctp)
00692                                 continue;
00693                         /*
00694                          * We found a DB handle that is part of this
00695                          * environment.  Close it.
00696                          */
00697                         __db_close_int(dbctp->ct_id, 0);
00698                         /*
00699                          * If we timed out a dbp, we may have removed
00700                          * multiple ctp entries.  Start over with a
00701                          * guaranteed good ctp.
00702                          */
00703                         nextctp = LIST_FIRST(&__dbsrv_head);
00704                 }
00705         ret = dbenv->close(dbenv, flags);
00706         __dbdel_ctp(ctp);
00707         return (ret);
00708 }
00709 
00710 static int
00711 add_home(home)
00712         char *home;
00713 {
00714         home_entry *hp, *homep;
00715         int ret;
00716 
00717         if ((ret = __os_malloc(NULL, sizeof(home_entry), &hp)) != 0)
00718                 return (ret);
00719         if ((ret = __os_malloc(NULL, strlen(home)+1, &hp->home)) != 0)
00720                 return (ret);
00721         memcpy(hp->home, home, strlen(home)+1);
00722         hp->dir = home;
00723         hp->passwd = NULL;
00724         /*
00725          * This loop is to remove any trailing path separators,
00726          * to assure hp->name points to the last component.
00727          */
00728         hp->name = __db_rpath(home);
00729         if (hp->name != NULL) {
00730                 *(hp->name) = '\0';
00731                 hp->name++;
00732         } else
00733                 hp->name = home;
00734         while (*(hp->name) == '\0') {
00735                 hp->name = __db_rpath(home);
00736                 *(hp->name) = '\0';
00737                 hp->name++;
00738         }
00739         /*
00740          * Now we have successfully added it.  Make sure there are no
00741          * identical names.
00742          */
00743         for (homep = LIST_FIRST(&__dbsrv_home); homep != NULL;
00744             homep = LIST_NEXT(homep, entries))
00745                 if (strcmp(homep->name, hp->name) == 0) {
00746                         printf("Already added home name %s, at directory %s\n",
00747                             hp->name, homep->dir);
00748                         __os_free(NULL, hp->home);
00749                         __os_free(NULL, hp);
00750                         return (-1);
00751                 }
00752         LIST_INSERT_HEAD(&__dbsrv_home, hp, entries);
00753         if (__dbsrv_verbose)
00754                 printf("Added home %s in dir %s\n", hp->name, hp->dir);
00755         return (0);
00756 }
00757 
00758 static int
00759 add_passwd(passwd)
00760         char *passwd;
00761 {
00762         home_entry *hp;
00763 
00764         /*
00765          * We add the passwd to the last given home dir.  If there
00766          * isn't a home dir, or the most recent one already has a
00767          * passwd, then there is a user error.
00768          */
00769         hp = LIST_FIRST(&__dbsrv_home);
00770         if (hp == NULL || hp->passwd != NULL)
00771                 return (EINVAL);
00772         /*
00773          * We've already strdup'ed the passwd above, so we don't need
00774          * to malloc new space, just point to it.
00775          */
00776         hp->passwd = passwd;
00777         return (0);
00778 }
00779 
00780 /*
00781  * PUBLIC: home_entry *get_fullhome __P((char *));
00782  */
00783 home_entry *
00784 get_fullhome(name)
00785         char *name;
00786 {
00787         home_entry *hp;
00788 
00789         if (name == NULL)
00790                 return (NULL);
00791         for (hp = LIST_FIRST(&__dbsrv_home); hp != NULL;
00792             hp = LIST_NEXT(hp, entries))
00793                 if (strcmp(name, hp->name) == 0)
00794                         return (hp);
00795         return (NULL);
00796 }
00797 
00798 static int
00799 env_recover(progname)
00800         char *progname;
00801 {
00802         DB_ENV *dbenv;
00803         home_entry *hp;
00804         u_int32_t flags;
00805         int exitval, ret;
00806 
00807         for (hp = LIST_FIRST(&__dbsrv_home); hp != NULL;
00808             hp = LIST_NEXT(hp, entries)) {
00809                 exitval = 0;
00810                 if ((ret = db_env_create(&dbenv, 0)) != 0) {
00811                         fprintf(stderr, "%s: db_env_create: %s\n",
00812                             progname, db_strerror(ret));
00813                         exit(EXIT_FAILURE);
00814                 }
00815                 if (__dbsrv_verbose == 1)
00816                         (void)dbenv->set_verbose(dbenv, DB_VERB_RECOVERY, 1);
00817                 dbenv->set_errfile(dbenv, stderr);
00818                 dbenv->set_errpfx(dbenv, progname);
00819                 if (hp->passwd != NULL)
00820                         (void)dbenv->set_encrypt(dbenv, hp->passwd,
00821                             DB_ENCRYPT_AES);
00822 
00823                 /*
00824                  * Initialize the env with DB_RECOVER.  That is all we
00825                  * have to do to run recovery.
00826                  */
00827                 if (__dbsrv_verbose)
00828                         printf("Running recovery on %s\n", hp->home);
00829                 flags = DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL |
00830                     DB_INIT_TXN | DB_USE_ENVIRON | DB_RECOVER;
00831                 if ((ret = dbenv->open(dbenv, hp->home, flags, 0)) != 0) {
00832                         dbenv->err(dbenv, ret, "DB_ENV->open");
00833                         goto error;
00834                 }
00835 
00836                 if (0) {
00837 error:                  exitval = 1;
00838                 }
00839                 if ((ret = dbenv->close(dbenv, 0)) != 0) {
00840                         exitval = 1;
00841                         fprintf(stderr, "%s: dbenv->close: %s\n",
00842                             progname, db_strerror(ret));
00843                 }
00844                 if (exitval)
00845                         return (exitval);
00846         }
00847         return (0);
00848 }

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