2e946737317cf9e377e8c19cc24a7ba3ad0446bd
[kopensolaris-gnu/glibc.git] / db2 / common / db_apprec.c
1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1996, 1997
5  *      Sleepycat Software.  All rights reserved.
6  */
7
8 #include "config.h"
9
10 #ifndef lint
11 static const char copyright[] =
12 "@(#) Copyright (c) 1997\n\
13         Sleepycat Software Inc.  All rights reserved.\n";
14 static const char sccsid[] = "@(#)db_apprec.c   10.16 (Sleepycat) 8/27/97";
15 #endif
16
17 #ifndef NO_SYSTEM_INCLUDES
18 #include <sys/types.h>
19
20 #include <time.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #endif
24
25 #include "db_int.h"
26 #include "shqueue.h"
27 #include "db_page.h"
28 #include "db_dispatch.h"
29 #include "db_am.h"
30 #include "log.h"
31 #include "txn.h"
32 #include "common_ext.h"
33
34 #define FREE_DBT(L, D) {                                                \
35         if (F_ISSET((L), DB_AM_THREAD) && (D).data != NULL)             \
36                 free((D).data);                                         \
37                 (D).data = NULL;                                        \
38         }                                                               \
39
40 /*
41  * __db_apprec --
42  *      Perform recovery.
43  *
44  * PUBLIC: int __db_apprec __P((DB_ENV *, int));
45  */
46 int
47 __db_apprec(dbenv, flags)
48         DB_ENV *dbenv;
49         int flags;
50 {
51         DBT data;
52         DB_LOG *lp;
53         DB_LSN ckp_lsn, first_lsn, lsn, tmp_lsn;
54         time_t now;
55         int first_flag, ret;
56         void *txninfo;
57
58         /* Initialize the transaction list. */
59         if ((ret = __db_txnlist_init(&txninfo)) != 0)
60                 return (ret);
61
62         /*
63          * Read forward through the log, opening the appropriate files so that
64          * we can call recovery routines.  In general, we start at the last
65          * checkpoint prior to the last checkpointed LSN.  For catastrophic
66          * recovery, we begin at the first LSN that appears in any log file
67          * (log_get figures this out for us when we pass it the DB_FIRST flag).
68          */
69         lp = dbenv->lg_info;
70         if (LF_ISSET(DB_RECOVER_FATAL))
71                 first_flag = DB_FIRST;
72         else {
73                 if ((ret = __log_findckp(lp, &lsn)) == DB_NOTFOUND)
74                         return (0);
75                 first_flag = DB_SET;
76         }
77
78         /* If we're a threaded application, we have to allocate space. */
79         memset(&data, 0, sizeof(data));
80         if (F_ISSET(lp, DB_AM_THREAD))
81                 F_SET(&data, DB_DBT_MALLOC);
82
83         if ((ret = log_get(lp, &lsn, &data, first_flag)) != 0) {
84                 __db_err(dbenv, "Failure: unable to get log record");
85                 if (first_flag == DB_SET)
86                         __db_err(dbenv, "Retrieving LSN %lu %lu",
87                             (u_long)lsn.file, (u_long)lsn.offset);
88                 else
89                         __db_err(dbenv, "Retrieving first LSN");
90                 goto err;
91         }
92
93         first_lsn = lsn;
94         for (;;) {
95                 ret = __db_dispatch(lp, &data, &lsn, TXN_OPENFILES, txninfo);
96                 FREE_DBT(lp, data);
97                 if (ret != 0 && ret != DB_TXN_CKP)
98                         goto msgerr;
99                 if ((ret =
100                     log_get(dbenv->lg_info, &lsn, &data, DB_NEXT)) != 0) {
101                         if (ret != DB_NOTFOUND)
102                                 goto err;
103                         break;
104                 }
105         }
106         FREE_DBT(lp, data);
107
108         /*
109          * Initialize the ckp_lsn to 0,0.  If we never find a valid
110          * checkpoint in the log, then leaving ckp_lsn at 0,0 is correct.
111          */
112         ZERO_LSN(ckp_lsn);
113         for (ret = log_get(lp, &lsn, &data, DB_LAST);
114             ret == 0 && log_compare(&lsn, &first_lsn) > 0;
115             ret = log_get(lp,&lsn, &data, DB_PREV)) {
116                 tmp_lsn = lsn;
117                 ret = __db_dispatch(lp,
118                     &data, &lsn, TXN_BACKWARD_ROLL, txninfo);
119                 FREE_DBT(lp, data);
120                 if (ret == DB_TXN_CKP) {
121                         if (IS_ZERO_LSN(ckp_lsn))
122                                 ckp_lsn = tmp_lsn;
123                         ret = 0;
124                 } else if (ret != 0)
125                         goto msgerr;
126         }
127         FREE_DBT(lp, data);
128         if (ret != 0 && ret != DB_NOTFOUND)
129                 goto err;
130
131         for (ret = log_get(lp, &lsn, &data, DB_NEXT);
132             ret == 0; ret = log_get(lp, &lsn, &data, DB_NEXT)) {
133                 ret = __db_dispatch(lp, &data, &lsn, TXN_FORWARD_ROLL, txninfo);
134                 FREE_DBT(lp, data);
135                 if (ret == DB_TXN_CKP)
136                         ret = 0;
137                 else if (ret != 0)
138                         goto msgerr;
139         }
140         FREE_DBT(lp, data);
141         if (ret != DB_NOTFOUND)
142                 goto err;
143
144         /* Now close all the db files that are open. */
145         __log_close_files(lp);
146
147         /*
148          * Now set the maximum transaction id, set the last checkpoint lsn,
149          * and the current time.  Then take a checkpoint.
150          */
151         (void)time(&now);
152         dbenv->tx_info->region->last_txnid = ((__db_txnhead *)txninfo)->maxid;
153         dbenv->tx_info->region->last_ckp = ckp_lsn;
154         dbenv->tx_info->region->time_ckp = (u_int32_t)now;
155         if ((ret = txn_checkpoint(dbenv->tx_info, 0, 0)) != 0)
156                 goto err;
157
158         if (dbenv->db_verbose) {
159                 __db_err(lp->dbenv, "Recovery complete at %s", ctime(&now));
160                 __db_err(lp->dbenv, "%s %lu %s [%lu][%lu]",
161                     "Maximum transaction id",
162                     (u_long)dbenv->tx_info->region->last_txnid,
163                     "Recovery checkpoint",
164                     (u_long)dbenv->tx_info->region->last_ckp.file,
165                     (u_long)dbenv->tx_info->region->last_ckp.offset);
166         }
167
168         return (0);
169
170 msgerr: __db_err(dbenv, "Recovery function for LSN %lu %lu failed",
171             (u_long)lsn.file, (u_long)lsn.offset);
172
173 err:    FREE_DBT(lp, data);
174         return (ret);
175 }