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