log.c

Go to the documentation of this file.
00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 1996, 1997, 1998, 1999, 2000
00005  *      Sleepycat Software.  All rights reserved.
00006  */
00007 #include "config.h"
00008 
00009 #ifndef lint
00010 static const char revid[] = "$Id: log_8c-source.html,v 1.1 2008/06/08 10:20:08 sebdiaz Exp $";
00011 #endif /* not lint */
00012 
00013 #ifndef NO_SYSTEM_INCLUDES
00014 #include <sys/types.h>
00015 
00016 #include <errno.h>
00017 #include <stdlib.h>
00018 #include <string.h>
00019 #include <unistd.h>
00020 #endif
00021 
00022 #ifdef  HAVE_RPC
00023 #include "db_server.h"
00024 #endif
00025 
00026 #include "db_int.h"
00027 #include "log.h"
00028 #include "db_dispatch.h"
00029 #include "txn.h"
00030 #include "txn_auto.h"
00031 
00032 #ifdef HAVE_RPC
00033 #include "gen_client_ext.h"
00034 #include "rpc_client_ext.h"
00035 #endif
00036 
00037 static int __log_init __P((DB_ENV *, DB_LOG *));
00038 static int __log_recover __P((DB_LOG *));
00039 
00040 /*
00041  * CDB___log_open --
00042  *      Internal version of log_open: only called from DB_ENV->open.
00043  *
00044  * PUBLIC: int CDB___log_open __P((DB_ENV *));
00045  */
00046 int
00047 CDB___log_open(dbenv)
00048         DB_ENV *dbenv;
00049 {
00050         DB_LOG *dblp;
00051         LOG *lp;
00052         int ret;
00053         u_int8_t *readbufp;
00054 
00055         readbufp = NULL;
00056 
00057         /* Create/initialize the DB_LOG structure. */
00058         if ((ret = CDB___os_calloc(dbenv, 1, sizeof(DB_LOG), &dblp)) != 0)
00059                 return (ret);
00060         if ((ret = CDB___os_calloc(dbenv, 1, dbenv->lg_bsize, &readbufp)) != 0)
00061                 goto err;
00062         ZERO_LSN(dblp->c_lsn);
00063         dblp->dbenv = dbenv;
00064 
00065         /* Join/create the log region. */
00066         dblp->reginfo.id = REG_ID_LOG;
00067         dblp->reginfo.mode = dbenv->db_mode;
00068         if (F_ISSET(dbenv, DB_ENV_CREATE))
00069                 F_SET(&dblp->reginfo, REGION_CREATE_OK);
00070         if ((ret = CDB___db_r_attach(
00071             dbenv, &dblp->reginfo, LG_BASE_REGION_SIZE + dbenv->lg_bsize)) != 0)
00072                 goto err;
00073 
00074         dblp->readbufp = readbufp;
00075 
00076         /* If we created the region, initialize it. */
00077         if (F_ISSET(&dblp->reginfo, REGION_CREATE) &&
00078             (ret = __log_init(dbenv, dblp)) != 0)
00079                 goto err;
00080 
00081         /* Set the local addresses. */
00082         lp = dblp->reginfo.primary =
00083             R_ADDR(&dblp->reginfo, dblp->reginfo.rp->primary);
00084         dblp->bufp = R_ADDR(&dblp->reginfo, lp->buffer_off);
00085 
00086         R_UNLOCK(dbenv, &dblp->reginfo);
00087 
00088         /*
00089          * If the region is threaded, then we have to lock both the handles
00090          * and the region, and we need to allocate a mutex for that purpose.
00091          */
00092         if (F_ISSET(dbenv, DB_ENV_THREAD)) {
00093                 if ((ret = CDB___db_mutex_alloc(
00094                     dbenv, &dblp->reginfo, &dblp->mutexp)) != 0)
00095                         goto detach;
00096                 if ((ret = __db_mutex_init(
00097                     dbenv, dblp->mutexp, 0, MUTEX_THREAD)) != 0)
00098                         goto detach;
00099         }
00100 
00101         dblp->r_file = 0;
00102         dblp->r_off = 0;
00103         dblp->r_size = 0;
00104         dbenv->lg_handle = dblp;
00105         return (0);
00106 
00107 err:    if (dblp->reginfo.addr != NULL) {
00108                 if (F_ISSET(&dblp->reginfo, REGION_CREATE))
00109                         F_SET(dblp->reginfo.rp, REG_DEAD);
00110                 R_UNLOCK(dbenv, &dblp->reginfo);
00111 
00112 detach:         (void)CDB___db_r_detach(dbenv, &dblp->reginfo, 0);
00113         }
00114 
00115         if (readbufp != NULL)
00116                 CDB___os_free(readbufp, sizeof(dbenv->lg_bsize));
00117         CDB___os_free(dblp, sizeof(*dblp));
00118         return (ret);
00119 }
00120 
00121 /*
00122  * __log_init --
00123  *      Initialize a log region in shared memory.
00124  */
00125 static int
00126 __log_init(dbenv, dblp)
00127         DB_ENV *dbenv;
00128         DB_LOG *dblp;
00129 {
00130         LOG *region;
00131         int ret;
00132         void *p;
00133 
00134         if ((ret = CDB___db_shalloc(dblp->reginfo.addr,
00135             sizeof(*region), 0, &dblp->reginfo.primary)) != 0)
00136                 goto mem_err;
00137         dblp->reginfo.rp->primary =
00138             R_OFFSET(&dblp->reginfo, dblp->reginfo.primary);
00139         region = dblp->reginfo.primary;
00140         memset(region, 0, sizeof(*region));
00141 
00142         region->persist.lg_max = dbenv->lg_max;
00143         region->persist.magic = DB_LOGMAGIC;
00144         region->persist.version = DB_LOGVERSION;
00145         region->persist.mode = dbenv->db_mode;
00146         SH_TAILQ_INIT(&region->fq);
00147 
00148         /* Initialize LOG LSNs. */
00149         region->lsn.file = 1;
00150         region->lsn.offset = 0;
00151 
00152         /* Initialize the buffer. */
00153         if ((ret =
00154             CDB___db_shalloc(dblp->reginfo.addr, dbenv->lg_bsize, 0, &p)) != 0) {
00155 mem_err:        CDB___db_err(dbenv, "Unable to allocate memory for the log buffer");
00156                 return (ret);
00157         }
00158         region->buffer_size = dbenv->lg_bsize;
00159         region->buffer_off = R_OFFSET(&dblp->reginfo, p);
00160 
00161         /*
00162          * XXX:
00163          * Initialize the log file size.  This is a hack to push the log's
00164          * maximum size down into the Windows CDB___os_open routine, because it
00165          * wants to pre-allocate it.
00166          */
00167         dblp->lfh.log_size = dbenv->lg_max;
00168 
00169         /* Try and recover any previous log files before releasing the lock. */
00170         return (__log_recover(dblp));
00171 }
00172 
00173 /*
00174  * __log_recover --
00175  *      Recover a log.
00176  */
00177 static int
00178 __log_recover(dblp)
00179         DB_LOG *dblp;
00180 {
00181         DBT dbt;
00182         DB_LSN lsn;
00183         LOG *lp;
00184         u_int32_t chk;
00185         int cnt, found_checkpoint, ret;
00186 
00187         lp = dblp->reginfo.primary;
00188 
00189         /*
00190          * Find a log file.  If none exist, we simply return, leaving
00191          * everything initialized to a new log.
00192          */
00193         if ((ret = CDB___log_find(dblp, 0, &cnt)) != 0)
00194                 return (ret);
00195         if (cnt == 0)
00196                 return (0);
00197 
00198         /*
00199          * We have the last useful log file and we've loaded any persistent
00200          * information.  Set the end point of the log past the end of the last
00201          * file. Read the last file, looking for the last checkpoint and
00202          * the log's end.
00203          */
00204         lp->lsn.file = cnt + 1;
00205         lp->lsn.offset = 0;
00206         lsn.file = cnt;
00207         lsn.offset = 0;
00208 
00209         /* Set the cursor.  Shouldn't fail, leave error messages on. */
00210         memset(&dbt, 0, sizeof(dbt));
00211         if ((ret = CDB___log_get(dblp, &lsn, &dbt, DB_SET, 0)) != 0)
00212                 return (ret);
00213 
00214         /*
00215          * Read to the end of the file, saving checkpoints.  This will fail
00216          * at some point, so turn off error messages.
00217          */
00218         found_checkpoint = 0;
00219         while (CDB___log_get(dblp, &lsn, &dbt, DB_NEXT, 1) == 0) {
00220                 if (dbt.size < sizeof(u_int32_t))
00221                         continue;
00222                 memcpy(&chk, dbt.data, sizeof(u_int32_t));
00223                 if (chk == DB_txn_ckp) {
00224                         lp->chkpt_lsn = lsn;
00225                         found_checkpoint = 1;
00226                 }
00227         }
00228 
00229         /*
00230          * We now know where the end of the log is.  Set the first LSN that
00231          * we want to return to an application and the LSN of the last known
00232          * record on disk.
00233          */
00234         lp->lsn = lp->s_lsn = lsn;
00235         lp->lsn.offset += dblp->c_len;
00236 
00237         /* Set up the current buffer information, too. */
00238         lp->len = dblp->c_len;
00239         lp->b_off = 0;
00240         lp->w_off = lp->lsn.offset;
00241 
00242         /*
00243          * It's possible that we didn't find a checkpoint because there wasn't
00244          * one in the last log file.  Start searching.
00245          */
00246         if (!found_checkpoint && cnt > 1) {
00247                 lsn.file = cnt;
00248                 lsn.offset = 0;
00249 
00250                 /* Set the cursor.  Shouldn't fail, leave error messages on. */
00251                 if ((ret = CDB___log_get(dblp, &lsn, &dbt, DB_SET, 0)) != 0)
00252                         return (ret);
00253 
00254                 /*
00255                  * Read to the end of the file, saving checkpoints.  Again,
00256                  * this can fail if there are no checkpoints in any log file,
00257                  * so turn error messages off.
00258                  */
00259                 while (CDB___log_get(dblp, &lsn, &dbt, DB_PREV, 1) == 0) {
00260                         if (dbt.size < sizeof(u_int32_t))
00261                                 continue;
00262                         memcpy(&chk, dbt.data, sizeof(u_int32_t));
00263                         if (chk == DB_txn_ckp) {
00264                                 lp->chkpt_lsn = lsn;
00265                                 found_checkpoint = 1;
00266                                 break;
00267                         }
00268                 }
00269         }
00270 
00271         /* If we never find a checkpoint, that's okay, just 0 it out. */
00272         if (!found_checkpoint)
00273                 ZERO_LSN(lp->chkpt_lsn);
00274 
00275         /*
00276          * Reset the cursor lsn to the beginning of the log, so that an
00277          * initial call to DB_NEXT does the right thing.
00278          */
00279         ZERO_LSN(dblp->c_lsn);
00280 
00281         if (FLD_ISSET(dblp->dbenv->verbose, DB_VERB_RECOVERY))
00282                 CDB___db_err(dblp->dbenv,
00283                     "Finding last valid log LSN: file: %lu offset %lu",
00284                     (u_long)lp->lsn.file, (u_long)lp->lsn.offset);
00285 
00286         return (0);
00287 }
00288 
00289 /*
00290  * CDB___log_find --
00291  *      Try to find a log file.  If find_first is set, valp will contain
00292  * the number of the first log file, else it will contain the number of
00293  * the last log file.
00294  *
00295  * PUBLIC: int CDB___log_find __P((DB_LOG *, int, int *));
00296  */
00297 int
00298 CDB___log_find(dblp, find_first, valp)
00299         DB_LOG *dblp;
00300         int find_first, *valp;
00301 {
00302         u_int32_t clv, logval;
00303         int cnt, fcnt, ret;
00304         const char *dir;
00305         char **names, *p, *q;
00306 
00307         /* Return a value of 0 as the log file number on failure. */
00308         *valp = 0;
00309 
00310         /* Find the directory name. */
00311         if ((ret = CDB___log_name(dblp, 1, &p, NULL, 0)) != 0)
00312                 return (ret);
00313         if ((q = CDB___db_rpath(p)) == NULL)
00314                 dir = PATH_DOT;
00315         else {
00316                 *q = '\0';
00317                 dir = p;
00318         }
00319 
00320         /* Get the list of file names. */
00321         ret = CDB___os_dirlist(dblp->dbenv, dir, &names, &fcnt);
00322 
00323         /*
00324          * !!!
00325          * We overwrote a byte in the string with a nul.  We have to restore
00326          * the string so that the diagnostic checks in the memory allocation
00327          * code work.
00328          */
00329         if (q != NULL)
00330                 *q = 'a';
00331 
00332         if (ret != 0) {
00333                 CDB___db_err(dblp->dbenv, "%s: %s", dir, CDB_db_strerror(ret));
00334                 CDB___os_freestr(p);
00335                 return (ret);
00336         }
00337 
00338         /*
00339          * Search for a valid log file name.
00340          *
00341          * XXX
00342          * Assumes that atoi(3) returns a 32-bit number.
00343          */
00344         for (cnt = fcnt, clv = logval = 0; --cnt >= 0;) {
00345                 if (strncmp(names[cnt], LFPREFIX, sizeof(LFPREFIX) - 1) != 0)
00346                         continue;
00347 
00348                 clv = atoi(names[cnt] + (sizeof(LFPREFIX) - 1));
00349                 if (find_first) {
00350                         if (logval != 0 && clv > logval)
00351                                 continue;
00352                 } else
00353                         if (logval != 0 && clv < logval)
00354                                 continue;
00355 
00356                 if (CDB___log_valid(dblp, clv, 1) == 0)
00357                         logval = clv;
00358         }
00359 
00360         *valp = logval;
00361 
00362         CDB___os_dirfree(names, fcnt);
00363         CDB___os_freestr(p);
00364 
00365         return (0);
00366 }
00367 
00368 /*
00369  * log_valid --
00370  *      Validate a log file.
00371  *
00372  * PUBLIC: int CDB___log_valid __P((DB_LOG *, u_int32_t, int));
00373  */
00374 int
00375 CDB___log_valid(dblp, number, set_persist)
00376         DB_LOG *dblp;
00377         u_int32_t number;
00378         int set_persist;
00379 {
00380         DB_FH fh;
00381         LOG *region;
00382         LOGP persist;
00383         size_t nw;
00384         int ret;
00385         char *fname;
00386 
00387         /* Try to open the log file. */
00388         if ((ret = CDB___log_name(dblp,
00389             number, &fname, &fh, DB_OSO_RDONLY | DB_OSO_SEQ)) != 0) {
00390                 CDB___os_freestr(fname);
00391                 return (ret);
00392         }
00393 
00394         /* Try to read the header. */
00395         if ((ret =
00396             CDB___os_seek(dblp->dbenv,
00397             &fh, 0, 0, sizeof(HDR), 0, DB_OS_SEEK_SET)) != 0 ||
00398             (ret =
00399             CDB___os_read(dblp->dbenv, &fh, &persist, sizeof(LOGP), &nw)) != 0 ||
00400             nw != sizeof(LOGP)) {
00401                 if (ret == 0)
00402                         ret = EIO;
00403 
00404                 (void)CDB___os_closehandle(&fh);
00405 
00406                 CDB___db_err(dblp->dbenv,
00407                     "Ignoring log file: %s: %s", fname, CDB_db_strerror(ret));
00408                 goto err;
00409         }
00410         (void)CDB___os_closehandle(&fh);
00411 
00412         /* Validate the header. */
00413         if (persist.magic != DB_LOGMAGIC) {
00414                 CDB___db_err(dblp->dbenv,
00415                     "Ignoring log file: %s: magic number %lx, not %lx",
00416                     fname, (u_long)persist.magic, (u_long)DB_LOGMAGIC);
00417                 ret = EINVAL;
00418                 goto err;
00419         }
00420         if (persist.version < DB_LOGOLDVER || persist.version > DB_LOGVERSION) {
00421                 CDB___db_err(dblp->dbenv,
00422                     "Ignoring log file: %s: unsupported log version %lu",
00423                     fname, (u_long)persist.version);
00424                 ret = EINVAL;
00425                 goto err;
00426         }
00427 
00428         /*
00429          * If we're going to use this log file, set the region's persistent
00430          * information based on the headers.
00431          */
00432         if (set_persist) {
00433                 region = dblp->reginfo.primary;
00434                 region->persist.lg_max = persist.lg_max;
00435                 region->persist.mode = persist.mode;
00436         }
00437         ret = 0;
00438 
00439 err:    CDB___os_freestr(fname);
00440         return (ret);
00441 }
00442 
00443 /*
00444  * CDB___log_close --
00445  *      Internal version of log_close: only called from dbenv_refresh.
00446  *
00447  * PUBLIC: int CDB___log_close __P((DB_ENV *));
00448  */
00449 int
00450 CDB___log_close(dbenv)
00451         DB_ENV *dbenv;
00452 {
00453         DB_LOG *dblp;
00454         int ret, t_ret;
00455 
00456         ret = 0;
00457         dblp = dbenv->lg_handle;
00458 
00459         /* We may have opened files as part of XA; if so, close them. */
00460         F_SET(dblp, DBLOG_RECOVER);
00461         CDB___log_close_files(dbenv);
00462 
00463         /* Discard the per-thread lock. */
00464         if (dblp->mutexp != NULL)
00465                 CDB___db_mutex_free(dbenv, &dblp->reginfo, dblp->mutexp);
00466 
00467         /* Detach from the region. */
00468         ret = CDB___db_r_detach(dbenv, &dblp->reginfo, 0);
00469 
00470         /* Close open files, release allocated memory. */
00471         if (F_ISSET(&dblp->lfh, DB_FH_VALID) &&
00472             (t_ret = CDB___os_closehandle(&dblp->lfh)) != 0 && ret == 0)
00473                 ret = t_ret;
00474         if (dblp->c_dbt.data != NULL)
00475                 CDB___os_free(dblp->c_dbt.data, dblp->c_dbt.ulen);
00476         if (F_ISSET(&dblp->c_fh, DB_FH_VALID) &&
00477             (t_ret = CDB___os_closehandle(&dblp->c_fh)) != 0 && ret == 0)
00478                 ret = t_ret;
00479         if (dblp->dbentry != NULL)
00480                 CDB___os_free(dblp->dbentry,
00481                     (dblp->dbentry_cnt * sizeof(DB_ENTRY)));
00482         if (dblp->readbufp != NULL)
00483                 CDB___os_free(dblp->readbufp, dbenv->lg_bsize);
00484 
00485         CDB___os_free(dblp, sizeof(*dblp));
00486 
00487         dbenv->lg_handle = NULL;
00488         return (ret);
00489 }
00490 
00491 /*
00492  * CDB_log_stat --
00493  *      Return LOG statistics.
00494  */
00495 int
00496 CDB_log_stat(dbenv, statp, db_malloc)
00497         DB_ENV *dbenv;
00498         DB_LOG_STAT **statp;
00499         void *(*db_malloc) __P((size_t));
00500 {
00501         DB_LOG *dblp;
00502         DB_LOG_STAT *stats;
00503         LOG *region;
00504         int ret;
00505 
00506 #ifdef HAVE_RPC
00507         if (F_ISSET(dbenv, DB_ENV_RPCCLIENT))
00508                 return (__dbcl_log_stat(dbenv, statp, db_malloc));
00509 #endif
00510 
00511         PANIC_CHECK(dbenv);
00512         ENV_REQUIRES_CONFIG(dbenv, dbenv->lg_handle, DB_INIT_LOG);
00513 
00514         *statp = NULL;
00515 
00516         dblp = dbenv->lg_handle;
00517         region = dblp->reginfo.primary;
00518 
00519         if ((ret = CDB___os_malloc(dbenv,
00520             sizeof(DB_LOG_STAT), db_malloc, &stats)) != 0)
00521                 return (ret);
00522 
00523         /* Copy out the global statistics. */
00524         R_LOCK(dbenv, &dblp->reginfo);
00525         *stats = region->stat;
00526 
00527         stats->st_magic = region->persist.magic;
00528         stats->st_version = region->persist.version;
00529         stats->st_mode = region->persist.mode;
00530         stats->st_lg_bsize = region->buffer_size;
00531         stats->st_lg_max = region->persist.lg_max;
00532 
00533         stats->st_region_wait = dblp->reginfo.rp->mutex.mutex_set_wait;
00534         stats->st_region_nowait = dblp->reginfo.rp->mutex.mutex_set_nowait;
00535         stats->st_regsize = dblp->reginfo.rp->size;
00536 
00537         stats->st_cur_file = region->lsn.file;
00538         stats->st_cur_offset = region->lsn.offset;
00539 
00540         R_UNLOCK(dbenv, &dblp->reginfo);
00541 
00542         *statp = stats;
00543         return (0);
00544 }

Generated on Sun Jun 8 10:56:38 2008 for GNUmifluz by  doxygen 1.5.5