Version information for db-2 library.
[kopensolaris-gnu/glibc.git] / db2 / log / log.c
1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1996, 1997, 1998
5  *      Sleepycat Software.  All rights reserved.
6  */
7 #include "config.h"
8
9 #ifndef lint
10 static const char sccsid[] = "@(#)log.c 10.54 (Sleepycat) 5/31/98";
11 #endif /* not lint */
12
13 #ifndef NO_SYSTEM_INCLUDES
14 #include <sys/types.h>
15
16 #include <errno.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <unistd.h>
20 #endif
21
22 #include "db_int.h"
23 #include "shqueue.h"
24 #include "log.h"
25 #include "db_dispatch.h"
26 #include "txn_auto.h"
27 #include "common_ext.h"
28
29 static int __log_recover __P((DB_LOG *));
30
31 /*
32  * log_open --
33  *      Initialize and/or join a log.
34  */
35 int
36 log_open(path, flags, mode, dbenv, lpp)
37         const char *path;
38         u_int32_t flags;
39         int mode;
40         DB_ENV *dbenv;
41         DB_LOG **lpp;
42 {
43         DB_LOG *dblp;
44         LOG *lp;
45         int ret;
46
47         /* Validate arguments. */
48 #ifdef HAVE_SPINLOCKS
49 #define OKFLAGS (DB_CREATE | DB_THREAD)
50 #else
51 #define OKFLAGS (DB_CREATE)
52 #endif
53         if ((ret = __db_fchk(dbenv, "log_open", flags, OKFLAGS)) != 0)
54                 return (ret);
55
56         /* Create and initialize the DB_LOG structure. */
57         if ((dblp = (DB_LOG *)__db_calloc(1, sizeof(DB_LOG))) == NULL)
58                 return (ENOMEM);
59
60         if (path != NULL && (dblp->dir = __db_strdup(path)) == NULL) {
61                 ret = ENOMEM;
62                 goto err;
63         }
64
65         dblp->dbenv = dbenv;
66         dblp->lfd = -1;
67         ZERO_LSN(dblp->c_lsn);
68         dblp->c_fd = -1;
69
70         /*
71          * The log region isn't fixed size because we store the registered
72          * file names there.  Make it fairly large so that we don't have to
73          * grow it.
74          */
75 #define DEF_LOG_SIZE    (30 * 1024)
76
77         /* Map in the region. */
78         dblp->reginfo.dbenv = dbenv;
79         dblp->reginfo.appname = DB_APP_LOG;
80         if (path == NULL)
81                 dblp->reginfo.path = NULL;
82         else
83                 if ((dblp->reginfo.path = __db_strdup(path)) == NULL)
84                         goto err;
85         dblp->reginfo.file = DB_DEFAULT_LOG_FILE;
86         dblp->reginfo.mode = mode;
87         dblp->reginfo.size = DEF_LOG_SIZE;
88         dblp->reginfo.dbflags = flags;
89         dblp->reginfo.flags = REGION_SIZEDEF;
90         if ((ret = __db_rattach(&dblp->reginfo)) != 0)
91                 goto err;
92
93         /*
94          * The LOG structure is first in the region, the rest of the region
95          * is free space.
96          */
97         dblp->lp = dblp->reginfo.addr;
98         dblp->addr = (u_int8_t *)dblp->lp + sizeof(LOG);
99
100         /* Initialize a created region. */
101         if (F_ISSET(&dblp->reginfo, REGION_CREATED)) {
102                 __db_shalloc_init(dblp->addr, DEF_LOG_SIZE - sizeof(LOG));
103
104                 /* Initialize the LOG structure. */
105                 lp = dblp->lp;
106                 lp->persist.lg_max = dbenv == NULL ? 0 : dbenv->lg_max;
107                 if (lp->persist.lg_max == 0)
108                         lp->persist.lg_max = DEFAULT_MAX;
109                 lp->persist.magic = DB_LOGMAGIC;
110                 lp->persist.version = DB_LOGVERSION;
111                 lp->persist.mode = mode;
112                 SH_TAILQ_INIT(&lp->fq);
113
114                 /* Initialize LOG LSNs. */
115                 lp->lsn.file = 1;
116                 lp->lsn.offset = 0;
117         }
118
119         /* Initialize thread information, mutex. */
120         if (LF_ISSET(DB_THREAD)) {
121                 F_SET(dblp, DB_AM_THREAD);
122                 if ((ret = __db_shalloc(dblp->addr,
123                     sizeof(db_mutex_t), MUTEX_ALIGNMENT, &dblp->mutexp)) != 0)
124                         goto err;
125                 (void)__db_mutex_init(dblp->mutexp, -1);
126         }
127
128         /*
129          * If doing recovery, try and recover any previous log files before
130          * releasing the lock.
131          */
132         if (F_ISSET(&dblp->reginfo, REGION_CREATED) &&
133             (ret = __log_recover(dblp)) != 0)
134                 goto err;
135
136         UNLOCK_LOGREGION(dblp);
137         *lpp = dblp;
138         return (0);
139
140 err:    if (dblp->reginfo.addr != NULL) {
141                 if (dblp->mutexp != NULL)
142                         __db_shalloc_free(dblp->addr, dblp->mutexp);
143
144                 UNLOCK_LOGREGION(dblp);
145                 (void)__db_rdetach(&dblp->reginfo);
146                 if (F_ISSET(&dblp->reginfo, REGION_CREATED))
147                         (void)log_unlink(path, 1, dbenv);
148         }
149
150         if (dblp->reginfo.path != NULL)
151                 FREES(dblp->reginfo.path);
152         if (dblp->dir != NULL)
153                 FREES(dblp->dir);
154         FREE(dblp, sizeof(*dblp));
155         return (ret);
156 }
157
158 /*
159  * __log_recover --
160  *      Recover a log.
161  */
162 static int
163 __log_recover(dblp)
164         DB_LOG *dblp;
165 {
166         DBT dbt;
167         DB_LSN lsn;
168         LOG *lp;
169         u_int32_t chk;
170         int cnt, found_checkpoint, ret;
171
172         lp = dblp->lp;
173
174         /*
175          * Find a log file.  If none exist, we simply return, leaving
176          * everything initialized to a new log.
177          */
178         if ((ret = __log_find(dblp, 0, &cnt)) != 0)
179                 return (ret);
180         if (cnt == 0)
181                 return (0);
182
183         /*
184          * We have the last useful log file and we've loaded any persistent
185          * information.  Pretend that the log is larger than it can possibly
186          * be, and read the last file, looking for the last checkpoint and
187          * the log's end.
188          */
189         lp->lsn.file = cnt + 1;
190         lp->lsn.offset = 0;
191         lsn.file = cnt;
192         lsn.offset = 0;
193
194         /* Set the cursor.  Shouldn't fail, leave error messages on. */
195         memset(&dbt, 0, sizeof(dbt));
196         if ((ret = __log_get(dblp, &lsn, &dbt, DB_SET, 0)) != 0)
197                 return (ret);
198
199         /*
200          * Read to the end of the file, saving checkpoints.  This will fail
201          * at some point, so turn off error messages.
202          */
203         found_checkpoint = 0;
204         while (__log_get(dblp, &lsn, &dbt, DB_NEXT, 1) == 0) {
205                 if (dbt.size < sizeof(u_int32_t))
206                         continue;
207                 memcpy(&chk, dbt.data, sizeof(u_int32_t));
208                 if (chk == DB_txn_ckp) {
209                         lp->chkpt_lsn = lsn;
210                         found_checkpoint = 1;
211                 }
212         }
213
214         /*
215          * We know where the end of the log is.  Since that record is on disk,
216          * it's also the last-synced LSN.
217          */
218         lp->lsn = lsn;
219         lp->lsn.offset += dblp->c_len;
220         lp->s_lsn = lp->lsn;
221
222         /* Set up the current buffer information, too. */
223         lp->len = dblp->c_len;
224         lp->b_off = 0;
225         lp->w_off = lp->lsn.offset;
226
227         /*
228          * It's possible that we didn't find a checkpoint because there wasn't
229          * one in the last log file.  Start searching.
230          */
231         while (!found_checkpoint && cnt > 1) {
232                 lsn.file = --cnt;
233                 lsn.offset = 0;
234
235                 /* Set the cursor.  Shouldn't fail, leave error messages on. */
236                 if ((ret = __log_get(dblp, &lsn, &dbt, DB_SET, 0)) != 0)
237                         return (ret);
238
239                 /*
240                  * Read to the end of the file, saving checkpoints.  Shouldn't
241                  * fail, leave error messages on.
242                  */
243                 while (__log_get(dblp, &lsn, &dbt, DB_NEXT, 0) == 0) {
244                         if (dbt.size < sizeof(u_int32_t))
245                                 continue;
246                         memcpy(&chk, dbt.data, sizeof(u_int32_t));
247                         if (chk == DB_txn_ckp) {
248                                 lp->chkpt_lsn = lsn;
249                                 found_checkpoint = 1;
250                         }
251                 }
252         }
253
254         /* If we never find a checkpoint, that's okay, just 0 it out. */
255         if (!found_checkpoint)
256                 ZERO_LSN(lp->chkpt_lsn);
257
258         __db_err(dblp->dbenv,
259             "Recovering the log: last valid LSN: file: %lu offset %lu",
260             (u_long)lp->lsn.file, (u_long)lp->lsn.offset);
261
262         return (0);
263 }
264
265 /*
266  * __log_find --
267  *      Try to find a log file.  If find_first is set, valp will contain
268  * the number of the first log file, else it will contain the number of
269  * the last log file.
270  *
271  * PUBLIC: int __log_find __P((DB_LOG *, int, int *));
272  */
273 int
274 __log_find(dblp, find_first, valp)
275         DB_LOG *dblp;
276         int find_first, *valp;
277 {
278         int cnt, fcnt, logval, ret;
279         const char *dir;
280         char **names, *p, *q;
281
282         *valp = 0;
283
284         /* Find the directory name. */
285         if ((ret = __log_name(dblp, 1, &p)) != 0)
286                 return (ret);
287         if ((q = __db_rpath(p)) == NULL)
288                 dir = PATH_DOT;
289         else {
290                 *q = '\0';
291                 dir = p;
292         }
293
294         /* Get the list of file names. */
295         ret = __db_dirlist(dir, &names, &fcnt);
296         FREES(p);
297         if (ret != 0) {
298                 __db_err(dblp->dbenv, "%s: %s", dir, strerror(ret));
299                 return (ret);
300         }
301
302         /*
303          * Search for a valid log file name, return a value of 0 on
304          * failure.
305          */
306         for (cnt = fcnt, logval = 0; --cnt >= 0;)
307                 if (strncmp(names[cnt], "log.", sizeof("log.") - 1) == 0) {
308                         logval = atoi(names[cnt] + 4);
309                         if (logval != 0 &&
310                             __log_valid(dblp, dblp->lp, logval) == 0)
311                                 break;
312                 }
313
314         /* Discard the list. */
315         __db_dirfree(names, fcnt);
316
317         /* We have a valid log file, find either the first or last one. */
318         if (find_first) {
319                 for (; logval > 0; --logval)
320                         if (__log_valid(dblp, dblp->lp, logval - 1) != 0)
321                                 break;
322         } else
323                 for (; logval < MAXLFNAME; ++logval)
324                         if (__log_valid(dblp, dblp->lp, logval + 1) != 0)
325                                 break;
326         *valp = logval;
327
328         return (0);
329 }
330
331 /*
332  * log_valid --
333  *      Validate a log file.
334  *
335  * PUBLIC: int __log_valid __P((DB_LOG *, LOG *, int));
336  */
337 int
338 __log_valid(dblp, lp, cnt)
339         DB_LOG *dblp;
340         LOG *lp;
341         int cnt;
342 {
343         LOGP persist;
344         ssize_t nw;
345         int fd, ret;
346         char *p;
347
348         if ((ret = __log_name(dblp, cnt, &p)) != 0)
349                 return (ret);
350
351         fd = -1;
352         if ((ret = __db_open(p,
353             DB_RDONLY | DB_SEQUENTIAL,
354             DB_RDONLY | DB_SEQUENTIAL, 0, &fd)) != 0 ||
355             (ret = __db_seek(fd, 0, 0, sizeof(HDR), 0, SEEK_SET)) != 0 ||
356             (ret = __db_read(fd, &persist, sizeof(LOGP), &nw)) != 0 ||
357             nw != sizeof(LOGP)) {
358                 if (ret == 0)
359                         ret = EIO;
360                 if (fd != -1) {
361                         (void)__db_close(fd);
362                         __db_err(dblp->dbenv,
363                             "Ignoring log file: %s: %s", p, strerror(ret));
364                 }
365                 goto err;
366         }
367         (void)__db_close(fd);
368
369         if (persist.magic != DB_LOGMAGIC) {
370                 __db_err(dblp->dbenv,
371                     "Ignoring log file: %s: magic number %lx, not %lx",
372                     p, (u_long)persist.magic, (u_long)DB_LOGMAGIC);
373                 ret = EINVAL;
374                 goto err;
375         }
376         if (persist.version < DB_LOGOLDVER || persist.version > DB_LOGVERSION) {
377                 __db_err(dblp->dbenv,
378                     "Ignoring log file: %s: unsupported log version %lu",
379                     p, (u_long)persist.version);
380                 ret = EINVAL;
381                 goto err;
382         }
383
384         if (lp != NULL) {
385                 lp->persist.lg_max = persist.lg_max;
386                 lp->persist.mode = persist.mode;
387         }
388         ret = 0;
389
390 err:    FREES(p);
391         return (ret);
392 }
393
394 /*
395  * log_close --
396  *      Close a log.
397  */
398 int
399 log_close(dblp)
400         DB_LOG *dblp;
401 {
402         int ret, t_ret;
403
404         /* Discard the per-thread pointer. */
405         if (dblp->mutexp != NULL) {
406                 LOCK_LOGREGION(dblp);
407                 __db_shalloc_free(dblp->addr, dblp->mutexp);
408                 UNLOCK_LOGREGION(dblp);
409         }
410
411         /* Close the region. */
412         ret = __db_rdetach(&dblp->reginfo);
413
414         /* Close open files, release allocated memory. */
415         if (dblp->lfd != -1 && (t_ret = __db_close(dblp->lfd)) != 0 && ret == 0)
416                 ret = t_ret;
417         if (dblp->c_dbt.data != NULL)
418                 FREE(dblp->c_dbt.data, dblp->c_dbt.ulen);
419         if (dblp->c_fd != -1 &&
420             (t_ret = __db_close(dblp->c_fd)) != 0 && ret == 0)
421                 ret = t_ret;
422         if (dblp->dbentry != NULL)
423                 FREE(dblp->dbentry, (dblp->dbentry_cnt * sizeof(DB_ENTRY)));
424         if (dblp->dir != NULL)
425                 FREES(dblp->dir);
426
427         if (dblp->reginfo.path != NULL)
428                 FREES(dblp->reginfo.path);
429         FREE(dblp, sizeof(*dblp));
430
431         return (ret);
432 }
433
434 /*
435  * log_unlink --
436  *      Exit a log.
437  */
438 int
439 log_unlink(path, force, dbenv)
440         const char *path;
441         int force;
442         DB_ENV *dbenv;
443 {
444         REGINFO reginfo;
445         int ret;
446
447         memset(&reginfo, 0, sizeof(reginfo));
448         reginfo.dbenv = dbenv;
449         reginfo.appname = DB_APP_LOG;
450         if (path != NULL && (reginfo.path = __db_strdup(path)) == NULL)
451                 return (ENOMEM);
452         reginfo.file = DB_DEFAULT_LOG_FILE;
453         ret = __db_runlink(&reginfo, force);
454         if (reginfo.path != NULL)
455                 FREES(reginfo.path);
456         return (ret);
457 }
458
459 /*
460  * log_stat --
461  *      Return LOG statistics.
462  */
463 int
464 log_stat(dblp, gspp, db_malloc)
465         DB_LOG *dblp;
466         DB_LOG_STAT **gspp;
467         void *(*db_malloc) __P((size_t));
468 {
469         LOG *lp;
470
471         *gspp = NULL;
472         lp = dblp->lp;
473
474         if ((*gspp = db_malloc == NULL ?
475             (DB_LOG_STAT *)__db_malloc(sizeof(**gspp)) :
476             (DB_LOG_STAT *)db_malloc(sizeof(**gspp))) == NULL)
477                 return (ENOMEM);
478
479         /* Copy out the global statistics. */
480         LOCK_LOGREGION(dblp);
481         **gspp = lp->stat;
482
483         (*gspp)->st_magic = lp->persist.magic;
484         (*gspp)->st_version = lp->persist.version;
485         (*gspp)->st_mode = lp->persist.mode;
486         (*gspp)->st_lg_max = lp->persist.lg_max;
487
488         (*gspp)->st_region_nowait = lp->rlayout.lock.mutex_set_nowait;
489         (*gspp)->st_region_wait = lp->rlayout.lock.mutex_set_wait;
490
491         (*gspp)->st_cur_file = lp->lsn.file;
492         (*gspp)->st_cur_offset = lp->lsn.offset;
493
494         (*gspp)->st_refcnt = lp->rlayout.refcnt;
495         (*gspp)->st_regsize = lp->rlayout.size;
496
497         UNLOCK_LOGREGION(dblp);
498
499         return (0);
500 }