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

log_archive.c

00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 1997-2005
00005  *      Sleepycat Software.  All rights reserved.
00006  *
00007  * $Id: log_archive.c,v 12.9 2005/11/04 17:27:58 ubell Exp $
00008  */
00009 
00010 #include "db_config.h"
00011 
00012 #ifndef NO_SYSTEM_INCLUDES
00013 #include <sys/types.h>
00014 
00015 #include <stdlib.h>
00016 #include <string.h>
00017 #endif
00018 
00019 #include "db_int.h"
00020 #include "dbinc/db_page.h"
00021 #include "dbinc/log.h"
00022 #include "dbinc/qam.h"
00023 #include "dbinc/txn.h"
00024 
00025 static int __absname __P((DB_ENV *, char *, char *, char **));
00026 static int __build_data __P((DB_ENV *, char *, char ***));
00027 static int __cmpfunc __P((const void *, const void *));
00028 static int __log_archive __P((DB_ENV *, char **[], u_int32_t));
00029 static int __usermem __P((DB_ENV *, char ***));
00030 
00031 /*
00032  * __log_archive_pp --
00033  *      DB_ENV->log_archive pre/post processing.
00034  *
00035  * PUBLIC: int __log_archive_pp __P((DB_ENV *, char **[], u_int32_t));
00036  */
00037 int
00038 __log_archive_pp(dbenv, listp, flags)
00039         DB_ENV *dbenv;
00040         char ***listp;
00041         u_int32_t flags;
00042 {
00043         DB_THREAD_INFO *ip;
00044         int ret;
00045 
00046         PANIC_CHECK(dbenv);
00047         ENV_REQUIRES_CONFIG(dbenv,
00048             dbenv->lg_handle, "DB_ENV->log_archive", DB_INIT_LOG);
00049 
00050 #define OKFLAGS (DB_ARCH_ABS | DB_ARCH_DATA | DB_ARCH_LOG | DB_ARCH_REMOVE)
00051         if (flags != 0) {
00052                 if ((ret = __db_fchk(
00053                     dbenv, "DB_ENV->log_archive", flags, OKFLAGS)) != 0)
00054                         return (ret);
00055                 if ((ret = __db_fcchk(dbenv, "DB_ENV->log_archive",
00056                     flags, DB_ARCH_DATA, DB_ARCH_LOG)) != 0)
00057                         return (ret);
00058                 if ((ret = __db_fcchk(dbenv, "DB_ENV->log_archive",
00059                     flags, DB_ARCH_REMOVE,
00060                     DB_ARCH_ABS | DB_ARCH_DATA | DB_ARCH_LOG)) != 0)
00061                         return (ret);
00062         }
00063 
00064         ENV_ENTER(dbenv, ip);
00065         REPLICATION_WRAP(dbenv, (__log_archive(dbenv, listp, flags)), ret);
00066         ENV_LEAVE(dbenv, ip);
00067         return (ret);
00068 }
00069 
00070 /*
00071  * __log_archive --
00072  *      DB_ENV->log_archive.  Internal.
00073  */
00074 static int
00075 __log_archive(dbenv, listp, flags)
00076         DB_ENV *dbenv;
00077         char ***listp;
00078         u_int32_t flags;
00079 {
00080         DBT rec;
00081         DB_LOG *dblp;
00082         LOG *lp;
00083         DB_LOGC *logc;
00084         DB_LSN stable_lsn;
00085         u_int array_size, n;
00086         u_int32_t fnum;
00087         int ret, t_ret;
00088         char **array, **arrayp, *name, *p, *pref;
00089 
00090         dblp = dbenv->lg_handle;
00091         lp = (LOG *)dblp->reginfo.primary;
00092         array = NULL;
00093         name = NULL;
00094         ret = 0;
00095         COMPQUIET(fnum, 0);
00096 
00097         if (flags != DB_ARCH_REMOVE)
00098                 *listp = NULL;
00099 
00100         /* There are no log files if logs are in memory. */
00101         if (lp->db_log_inmemory) {
00102                 LF_CLR(~DB_ARCH_DATA);
00103                 if (flags == 0)
00104                         return (0);
00105         }
00106 
00107         /*
00108          * If the user wants the list of log files to remove and we're
00109          * at a bad time in replication initialization, just return.
00110          */
00111         if (!LF_ISSET(DB_ARCH_DATA) &&
00112             !LF_ISSET(DB_ARCH_LOG) && __rep_noarchive(dbenv))
00113                 return (0);
00114 
00115         /*
00116          * Prepend the original absolute pathname if the user wants an
00117          * absolute path to the database environment directory.
00118          */
00119         pref = LF_ISSET(DB_ARCH_ABS) ? dbenv->db_abshome : NULL;
00120 
00121         LF_CLR(DB_ARCH_ABS);
00122         switch (flags) {
00123         case DB_ARCH_DATA:
00124                 ret = __build_data(dbenv, pref, listp);
00125                 goto err;
00126         case DB_ARCH_LOG:
00127                 memset(&rec, 0, sizeof(rec));
00128                 if ((ret = __log_cursor(dbenv, &logc)) != 0)
00129                         goto err;
00130 #ifdef UMRW
00131                 ZERO_LSN(stable_lsn);
00132 #endif
00133                 ret = __log_c_get(logc, &stable_lsn, &rec, DB_LAST);
00134                 if ((t_ret = __log_c_close(logc)) != 0 && ret == 0)
00135                         ret = t_ret;
00136                 if (ret != 0)
00137                         goto err;
00138                 fnum = stable_lsn.file;
00139                 break;
00140         case DB_ARCH_REMOVE:
00141                 __log_autoremove(dbenv);
00142                 goto err;
00143         case 0:
00144 
00145                 ret = __log_get_stable_lsn(dbenv, &stable_lsn);
00146                 /*
00147                  * A return of DB_NOTFOUND means the checkpoint LSN
00148                  * is before the beginning of the log files we have.
00149                  * This is not an error; it just means we're done.
00150                  */
00151                 if (ret != 0) {
00152                         if (ret == DB_NOTFOUND)
00153                                 ret = 0;
00154                         goto err;
00155                 }
00156                 /* Remove any log files before the last stable LSN. */
00157                 fnum = stable_lsn.file - 1;
00158                 break;
00159         default:
00160                 DB_ASSERT(0);
00161                 ret = EINVAL;
00162                 goto err;
00163         }
00164 
00165 #define LIST_INCREMENT  64
00166         /* Get some initial space. */
00167         array_size = 64;
00168         if ((ret = __os_malloc(dbenv,
00169             sizeof(char *) * array_size, &array)) != 0)
00170                 goto err;
00171         array[0] = NULL;
00172 
00173         /* Build an array of the file names. */
00174         for (n = 0; fnum > 0; --fnum) {
00175                 if ((ret = __log_name(dblp, fnum, &name, NULL, 0)) != 0)
00176                         goto err;
00177                 if (__os_exists(name, NULL) != 0) {
00178                         if (LF_ISSET(DB_ARCH_LOG) && fnum == stable_lsn.file)
00179                                 continue;
00180                         __os_free(dbenv, name);
00181                         name = NULL;
00182                         break;
00183                 }
00184 
00185                 if (n >= array_size - 2) {
00186                         array_size += LIST_INCREMENT;
00187                         if ((ret = __os_realloc(dbenv,
00188                             sizeof(char *) * array_size, &array)) != 0)
00189                                 goto err;
00190                 }
00191 
00192                 if (pref != NULL) {
00193                         if ((ret =
00194                             __absname(dbenv, pref, name, &array[n])) != 0)
00195                                 goto err;
00196                         __os_free(dbenv, name);
00197                 } else if ((p = __db_rpath(name)) != NULL) {
00198                         if ((ret = __os_strdup(dbenv, p + 1, &array[n])) != 0)
00199                                 goto err;
00200                         __os_free(dbenv, name);
00201                 } else
00202                         array[n] = name;
00203 
00204                 name = NULL;
00205                 array[++n] = NULL;
00206         }
00207 
00208         /* If there's nothing to return, we're done. */
00209         if (n == 0)
00210                 goto err;
00211 
00212         /* Sort the list. */
00213         qsort(array, (size_t)n, sizeof(char *), __cmpfunc);
00214 
00215         /* Rework the memory. */
00216         if ((ret = __usermem(dbenv, &array)) != 0)
00217                 goto err;
00218 
00219         if (listp != NULL)
00220                 *listp = array;
00221 
00222         if (0) {
00223 err:            if (array != NULL) {
00224                         for (arrayp = array; *arrayp != NULL; ++arrayp)
00225                                 __os_free(dbenv, *arrayp);
00226                         __os_free(dbenv, array);
00227                 }
00228                 if (name != NULL)
00229                         __os_free(dbenv, name);
00230         }
00231 
00232         return (ret);
00233 }
00234 
00235 /*
00236  * __log_get_stable_lsn --
00237  *      Get the stable lsn based on where checkpoints are.
00238  *
00239  * PUBLIC: int __log_get_stable_lsn __P((DB_ENV *, DB_LSN *));
00240  */
00241 int
00242 __log_get_stable_lsn(dbenv, stable_lsn)
00243         DB_ENV *dbenv;
00244         DB_LSN *stable_lsn;
00245 {
00246         DB_LOGC *logc;
00247         DBT rec;
00248         __txn_ckp_args *ckp_args;
00249         int ret, t_ret;
00250 
00251         ret = 0;
00252         memset(&rec, 0, sizeof(rec));
00253         if (!TXN_ON(dbenv)) {
00254                 if ((ret = __log_get_cached_ckp_lsn(dbenv, stable_lsn)) != 0)
00255                         goto err;
00256                 /*
00257                  * No need to check for a return value of DB_NOTFOUND;
00258                  * __txn_findlastckp returns 0 if no checkpoint record
00259                  * is found.  Instead of checking the return value, we
00260                  * check to see if the return LSN has been filled in.
00261                  */
00262                 if (IS_ZERO_LSN(*stable_lsn) && (ret =
00263                      __txn_findlastckp(dbenv, stable_lsn, NULL)) != 0)
00264                         goto err;
00265                 /*
00266                  * If the LSN has not been filled in return DB_NOTFOUND
00267                  * so that the caller knows it may be done.
00268                  */
00269                 if (IS_ZERO_LSN(*stable_lsn)) {
00270                         ret = DB_NOTFOUND;
00271                         goto err;
00272                 }
00273         } else if ((ret = __txn_getckp(dbenv, stable_lsn)) != 0)
00274                 goto err;
00275         if ((ret = __log_cursor(dbenv, &logc)) != 0)
00276                 goto err;
00277         /*
00278          * If we can read it, set the stable_lsn to the ckp_lsn in the
00279          * checkpoint record.
00280          */
00281         if ((ret = __log_c_get(logc, stable_lsn, &rec, DB_SET)) == 0 &&
00282             (ret = __txn_ckp_read(dbenv, rec.data, &ckp_args)) == 0) {
00283                 *stable_lsn = ckp_args->ckp_lsn;
00284                 __os_free(dbenv, ckp_args);
00285         }
00286         if ((t_ret = __log_c_close(logc)) != 0 && ret == 0)
00287                 ret = t_ret;
00288 err:
00289         return (ret);
00290 }
00291 
00292 /*
00293  * __log_autoremove --
00294  *      Delete any non-essential log files.
00295  *
00296  * PUBLIC: void __log_autoremove __P((DB_ENV *));
00297  */
00298 void
00299 __log_autoremove(dbenv)
00300         DB_ENV *dbenv;
00301 {
00302         int ret;
00303         char **begin, **list;
00304 
00305         /*
00306          * Complain if there's an error, but don't return the error to our
00307          * caller.  Auto-remove is done when writing a log record, and we
00308          * don't want to fail a write, which could fail the corresponding
00309          * committing transaction, for a permissions error.
00310          */
00311         if ((ret = __log_archive(dbenv, &list, DB_ARCH_ABS)) != 0) {
00312                 if (ret != DB_NOTFOUND)
00313                         __db_err(dbenv,
00314                             "log file auto-remove: %s", db_strerror(ret));
00315                 return;
00316         }
00317 
00318         /*
00319          * Remove the files.  No error message needed for __os_unlink failure,
00320          * the underlying OS layer has its own error handling.
00321          */
00322         if (list != NULL) {
00323                 for (begin = list; *list != NULL; ++list)
00324                         (void)__os_unlink(dbenv, *list);
00325                 __os_ufree(dbenv, begin);
00326         }
00327 }
00328 
00329 /*
00330  * __build_data --
00331  *      Build a list of datafiles for return.
00332  */
00333 static int
00334 __build_data(dbenv, pref, listp)
00335         DB_ENV *dbenv;
00336         char *pref, ***listp;
00337 {
00338         DBT rec;
00339         DB_LOGC *logc;
00340         DB_LSN lsn;
00341         __dbreg_register_args *argp;
00342         u_int array_size, last, n, nxt;
00343         u_int32_t rectype;
00344         int ret, t_ret;
00345         char **array, **arrayp, **list, **lp, *p, *real_name;
00346 
00347         /* Get some initial space. */
00348         array_size = 64;
00349         if ((ret = __os_malloc(dbenv,
00350             sizeof(char *) * array_size, &array)) != 0)
00351                 return (ret);
00352         array[0] = NULL;
00353 
00354         memset(&rec, 0, sizeof(rec));
00355         if ((ret = __log_cursor(dbenv, &logc)) != 0)
00356                 return (ret);
00357         for (n = 0; (ret = __log_c_get(logc, &lsn, &rec, DB_PREV)) == 0;) {
00358                 if (rec.size < sizeof(rectype)) {
00359                         ret = EINVAL;
00360                         __db_err(dbenv, "DB_ENV->log_archive: bad log record");
00361                         break;
00362                 }
00363 
00364                 memcpy(&rectype, rec.data, sizeof(rectype));
00365                 if (rectype != DB___dbreg_register)
00366                         continue;
00367                 if ((ret =
00368                     __dbreg_register_read(dbenv, rec.data, &argp)) != 0) {
00369                         ret = EINVAL;
00370                         __db_err(dbenv,
00371                             "DB_ENV->log_archive: unable to read log record");
00372                         break;
00373                 }
00374 
00375                 if (n >= array_size - 2) {
00376                         array_size += LIST_INCREMENT;
00377                         if ((ret = __os_realloc(dbenv,
00378                             sizeof(char *) * array_size, &array)) != 0)
00379                                 goto free_continue;
00380                 }
00381 
00382                 if ((ret = __os_strdup(dbenv,
00383                     argp->name.data, &array[n++])) != 0)
00384                         goto free_continue;
00385                 array[n] = NULL;
00386 
00387                 if (argp->ftype == DB_QUEUE) {
00388                         if ((ret = __qam_extent_names(dbenv,
00389                             argp->name.data, &list)) != 0)
00390                                 goto q_err;
00391                         for (lp = list;
00392                             lp != NULL && *lp != NULL; lp++) {
00393                                 if (n >= array_size - 2) {
00394                                         array_size += LIST_INCREMENT;
00395                                         if ((ret = __os_realloc(dbenv,
00396                                             sizeof(char *) *
00397                                             array_size, &array)) != 0)
00398                                                 goto q_err;
00399                                 }
00400                                 if ((ret =
00401                                     __os_strdup(dbenv, *lp, &array[n++])) != 0)
00402                                         goto q_err;
00403                                 array[n] = NULL;
00404                         }
00405 q_err:                  if (list != NULL)
00406                                 __os_free(dbenv, list);
00407                 }
00408 free_continue:  __os_free(dbenv, argp);
00409                 if (ret != 0)
00410                         break;
00411         }
00412         if (ret == DB_NOTFOUND)
00413                 ret = 0;
00414         if ((t_ret = __log_c_close(logc)) != 0 && ret == 0)
00415                 ret = t_ret;
00416         if (ret != 0)
00417                 goto err1;
00418 
00419         /* If there's nothing to return, we're done. */
00420         if (n == 0) {
00421                 ret = 0;
00422                 *listp = NULL;
00423                 goto err1;
00424         }
00425 
00426         /* Sort the list. */
00427         qsort(array, (size_t)n, sizeof(char *), __cmpfunc);
00428 
00429         /*
00430          * Build the real pathnames, discarding nonexistent files and
00431          * duplicates.
00432          */
00433         for (last = nxt = 0; nxt < n;) {
00434                 /*
00435                  * Discard duplicates.  Last is the next slot we're going
00436                  * to return to the user, nxt is the next slot that we're
00437                  * going to consider.
00438                  */
00439                 if (last != nxt) {
00440                         array[last] = array[nxt];
00441                         array[nxt] = NULL;
00442                 }
00443                 for (++nxt; nxt < n &&
00444                     strcmp(array[last], array[nxt]) == 0; ++nxt) {
00445                         __os_free(dbenv, array[nxt]);
00446                         array[nxt] = NULL;
00447                 }
00448 
00449                 /* Get the real name. */
00450                 if ((ret = __db_appname(dbenv,
00451                     DB_APP_DATA, array[last], 0, NULL, &real_name)) != 0)
00452                         goto err2;
00453 
00454                 /* If the file doesn't exist, ignore it. */
00455                 if (__os_exists(real_name, NULL) != 0) {
00456                         __os_free(dbenv, real_name);
00457                         __os_free(dbenv, array[last]);
00458                         array[last] = NULL;
00459                         continue;
00460                 }
00461 
00462                 /* Rework the name as requested by the user. */
00463                 __os_free(dbenv, array[last]);
00464                 array[last] = NULL;
00465                 if (pref != NULL) {
00466                         ret = __absname(dbenv, pref, real_name, &array[last]);
00467                         __os_free(dbenv, real_name);
00468                         if (ret != 0)
00469                                 goto err2;
00470                 } else if ((p = __db_rpath(real_name)) != NULL) {
00471                         ret = __os_strdup(dbenv, p + 1, &array[last]);
00472                         __os_free(dbenv, real_name);
00473                         if (ret != 0)
00474                                 goto err2;
00475                 } else
00476                         array[last] = real_name;
00477                 ++last;
00478         }
00479 
00480         /* NULL-terminate the list. */
00481         array[last] = NULL;
00482 
00483         /* Rework the memory. */
00484         if ((ret = __usermem(dbenv, &array)) != 0)
00485                 goto err1;
00486 
00487         *listp = array;
00488         return (0);
00489 
00490 err2:   /*
00491          * XXX
00492          * We've possibly inserted NULLs into the array list, so clean up a
00493          * bit so that the other error processing works.
00494          */
00495         if (array != NULL)
00496                 for (; nxt < n; ++nxt)
00497                         __os_free(dbenv, array[nxt]);
00498         /* FALLTHROUGH */
00499 
00500 err1:   if (array != NULL) {
00501                 for (arrayp = array; *arrayp != NULL; ++arrayp)
00502                         __os_free(dbenv, *arrayp);
00503                 __os_free(dbenv, array);
00504         }
00505         return (ret);
00506 }
00507 
00508 /*
00509  * __absname --
00510  *      Return an absolute path name for the file.
00511  */
00512 static int
00513 __absname(dbenv, pref, name, newnamep)
00514         DB_ENV *dbenv;
00515         char *pref, *name, **newnamep;
00516 {
00517         size_t l_pref, l_name;
00518         int isabspath, ret;
00519         char *newname;
00520 
00521         l_name = strlen(name);
00522         isabspath = __os_abspath(name);
00523         l_pref = isabspath ? 0 : strlen(pref);
00524 
00525         /* Malloc space for concatenating the two. */
00526         if ((ret = __os_malloc(dbenv,
00527             l_pref + l_name + 2, &newname)) != 0)
00528                 return (ret);
00529         *newnamep = newname;
00530 
00531         /* Build the name.  If `name' is an absolute path, ignore any prefix. */
00532         if (!isabspath) {
00533                 memcpy(newname, pref, l_pref);
00534                 if (strchr(PATH_SEPARATOR, newname[l_pref - 1]) == NULL)
00535                         newname[l_pref++] = PATH_SEPARATOR[0];
00536         }
00537         memcpy(newname + l_pref, name, l_name + 1);
00538 
00539         return (0);
00540 }
00541 
00542 /*
00543  * __usermem --
00544  *      Create a single chunk of memory that holds the returned information.
00545  *      If the user has their own malloc routine, use it.
00546  */
00547 static int
00548 __usermem(dbenv, listp)
00549         DB_ENV *dbenv;
00550         char ***listp;
00551 {
00552         size_t len;
00553         int ret;
00554         char **array, **arrayp, **orig, *strp;
00555 
00556         /* Find out how much space we need. */
00557         for (len = 0, orig = *listp; *orig != NULL; ++orig)
00558                 len += sizeof(char *) + strlen(*orig) + 1;
00559         len += sizeof(char *);
00560 
00561         /* Allocate it and set up the pointers. */
00562         if ((ret = __os_umalloc(dbenv, len, &array)) != 0)
00563                 return (ret);
00564 
00565         strp = (char *)(array + (orig - *listp) + 1);
00566 
00567         /* Copy the original information into the new memory. */
00568         for (orig = *listp, arrayp = array; *orig != NULL; ++orig, ++arrayp) {
00569                 len = strlen(*orig);
00570                 memcpy(strp, *orig, len + 1);
00571                 *arrayp = strp;
00572                 strp += len + 1;
00573 
00574                 __os_free(dbenv, *orig);
00575         }
00576 
00577         /* NULL-terminate the list. */
00578         *arrayp = NULL;
00579 
00580         __os_free(dbenv, *listp);
00581         *listp = array;
00582 
00583         return (0);
00584 }
00585 
00586 static int
00587 __cmpfunc(p1, p2)
00588         const void *p1, *p2;
00589 {
00590         return (strcmp(*((char * const *)p1), *((char * const *)p2)));
00591 }

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