2 * See the file LICENSE for redistribution information.
4 * Copyright (c) 1996, 1997, 1998
5 * Sleepycat Software. All rights reserved.
10 static const char sccsid
[] = "@(#)log.c 10.63 (Sleepycat) 10/10/98";
13 #ifndef NO_SYSTEM_INCLUDES
14 #include <sys/types.h>
26 #include "db_dispatch.h"
29 #include "common_ext.h"
31 static int __log_recover
__P((DB_LOG
*));
35 * Initialize and/or join a log.
38 log_open(path
, flags
, mode
, dbenv
, lpp
)
49 /* Validate arguments. */
51 #define OKFLAGS (DB_CREATE | DB_THREAD)
53 #define OKFLAGS (DB_CREATE)
55 if ((ret
= __db_fchk(dbenv
, "log_open", flags
, OKFLAGS
)) != 0)
58 /* Create and initialize the DB_LOG structure. */
59 if ((ret
= __os_calloc(1, sizeof(DB_LOG
), &dblp
)) != 0)
62 if (path
!= NULL
&& (ret
= __os_strdup(path
, &dblp
->dir
)) != 0)
67 ZERO_LSN(dblp
->c_lsn
);
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
75 #define DEF_LOG_SIZE (30 * 1024)
77 /* Map in the region. */
78 dblp
->reginfo
.dbenv
= dbenv
;
79 dblp
->reginfo
.appname
= DB_APP_LOG
;
81 dblp
->reginfo
.path
= NULL
;
83 if ((ret
= __os_strdup(path
, &dblp
->reginfo
.path
)) != 0)
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)
94 * The LOG structure is first in the region, the rest of the region
97 dblp
->lp
= dblp
->reginfo
.addr
;
98 dblp
->addr
= (u_int8_t
*)dblp
->lp
+ sizeof(LOG
);
100 /* Initialize a created region. */
101 if (F_ISSET(&dblp
->reginfo
, REGION_CREATED
)) {
102 __db_shalloc_init(dblp
->addr
, DEF_LOG_SIZE
- sizeof(LOG
));
104 /* Initialize the LOG structure. */
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
);
114 /* Initialize LOG LSNs. */
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)
125 (void)__db_mutex_init(dblp
->mutexp
, 0);
129 * If doing recovery, try and recover any previous log files before
130 * releasing the lock.
132 if (F_ISSET(&dblp
->reginfo
, REGION_CREATED
) &&
133 (ret
= __log_recover(dblp
)) != 0)
136 UNLOCK_LOGREGION(dblp
);
140 err
: if (dblp
->reginfo
.addr
!= NULL
) {
141 if (dblp
->mutexp
!= NULL
)
142 __db_shalloc_free(dblp
->addr
, dblp
->mutexp
);
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
);
150 if (dblp
->reginfo
.path
!= NULL
)
151 __os_freestr(dblp
->reginfo
.path
);
152 if (dblp
->dir
!= NULL
)
153 __os_freestr(dblp
->dir
);
154 __os_free(dblp
, sizeof(*dblp
));
162 * PUBLIC: void __log_panic __P((DB_ENV *));
168 if (dbenv
->lg_info
!= NULL
)
169 dbenv
->lg_info
->lp
->rlayout
.panic
= 1;
184 int cnt
, found_checkpoint
, ret
;
189 * Find a log file. If none exist, we simply return, leaving
190 * everything initialized to a new log.
192 if ((ret
= __log_find(dblp
, 0, &cnt
)) != 0)
198 * We have the last useful log file and we've loaded any persistent
199 * information. Pretend that the log is larger than it can possibly
200 * be, and read the last file, looking for the last checkpoint and
203 lp
->lsn
.file
= cnt
+ 1;
208 /* Set the cursor. Shouldn't fail, leave error messages on. */
209 memset(&dbt
, 0, sizeof(dbt
));
210 if ((ret
= __log_get(dblp
, &lsn
, &dbt
, DB_SET
, 0)) != 0)
214 * Read to the end of the file, saving checkpoints. This will fail
215 * at some point, so turn off error messages.
217 found_checkpoint
= 0;
218 while (__log_get(dblp
, &lsn
, &dbt
, DB_NEXT
, 1) == 0) {
219 if (dbt
.size
< sizeof(u_int32_t
))
221 memcpy(&chk
, dbt
.data
, sizeof(u_int32_t
));
222 if (chk
== DB_txn_ckp
) {
224 found_checkpoint
= 1;
229 * We now know where the end of the log is. Set the first LSN that
230 * we want to return to an application and the LSN of the last known
233 lp
->lsn
= lp
->s_lsn
= lsn
;
234 lp
->lsn
.offset
+= dblp
->c_len
;
236 /* Set up the current buffer information, too. */
237 lp
->len
= dblp
->c_len
;
239 lp
->w_off
= lp
->lsn
.offset
;
242 * It's possible that we didn't find a checkpoint because there wasn't
243 * one in the last log file. Start searching.
245 while (!found_checkpoint
&& cnt
> 1) {
249 /* Set the cursor. Shouldn't fail, leave error messages on. */
250 if ((ret
= __log_get(dblp
, &lsn
, &dbt
, DB_SET
, 0)) != 0)
254 * Read to the end of the file, saving checkpoints. Shouldn't
255 * fail, leave error messages on.
257 while (__log_get(dblp
, &lsn
, &dbt
, DB_NEXT
, 0) == 0) {
258 if (dbt
.size
< sizeof(u_int32_t
))
260 memcpy(&chk
, dbt
.data
, sizeof(u_int32_t
));
261 if (chk
== DB_txn_ckp
) {
263 found_checkpoint
= 1;
268 * Reset the cursor lsn to the beginning of the log, so that an
269 * initial call to DB_NEXT does the right thing.
271 ZERO_LSN(dblp
->c_lsn
);
273 /* If we never find a checkpoint, that's okay, just 0 it out. */
274 if (!found_checkpoint
)
275 ZERO_LSN(lp
->chkpt_lsn
);
279 * The test suite explicitly looks for this string -- don't change
280 * it here unless you also change it there.
282 __db_err(dblp
->dbenv
,
283 "Finding last valid log LSN: file: %lu offset %lu",
284 (u_long
)lp
->lsn
.file
, (u_long
)lp
->lsn
.offset
);
291 * Try to find a log file. If find_first is set, valp will contain
292 * the number of the first log file, else it will contain the number of
295 * PUBLIC: int __log_find __P((DB_LOG *, int, int *));
298 __log_find(dblp
, find_first
, valp
)
300 int find_first
, *valp
;
302 u_int32_t clv
, logval
;
305 char **names
, *p
, *q
;
309 /* Find the directory name. */
310 if ((ret
= __log_name(dblp
, 1, &p
, NULL
, 0)) != 0)
312 if ((q
= __db_rpath(p
)) == NULL
)
319 /* Get the list of file names. */
320 ret
= __os_dirlist(dir
, &names
, &fcnt
);
323 __db_err(dblp
->dbenv
, "%s: %s", dir
, strerror(ret
));
328 * Search for a valid log file name, return a value of 0 on
332 * Assumes that atoi(3) returns a 32-bit number.
334 for (cnt
= fcnt
, clv
= logval
= 0; --cnt
>= 0;) {
335 if (strncmp(names
[cnt
], LFPREFIX
, sizeof(LFPREFIX
) - 1) != 0)
338 clv
= atoi(names
[cnt
] + (sizeof(LFPREFIX
) - 1));
340 if (logval
!= 0 && clv
> logval
)
343 if (logval
!= 0 && clv
< logval
)
346 if (__log_valid(dblp
, clv
, 1) == 0)
352 /* Discard the list. */
353 __os_dirfree(names
, fcnt
);
360 * Validate a log file.
362 * PUBLIC: int __log_valid __P((DB_LOG *, u_int32_t, int));
365 __log_valid(dblp
, number
, set_persist
)
375 /* Try to open the log file. */
376 if ((ret
= __log_name(dblp
,
377 number
, &fname
, &fd
, DB_RDONLY
| DB_SEQUENTIAL
)) != 0) {
382 /* Try to read the header. */
383 if ((ret
= __os_seek(fd
, 0, 0, sizeof(HDR
), 0, SEEK_SET
)) != 0 ||
384 (ret
= __os_read(fd
, &persist
, sizeof(LOGP
), &nw
)) != 0 ||
385 nw
!= sizeof(LOGP
)) {
389 (void)__os_close(fd
);
391 __db_err(dblp
->dbenv
,
392 "Ignoring log file: %s: %s", fname
, strerror(ret
));
395 (void)__os_close(fd
);
397 /* Validate the header. */
398 if (persist
.magic
!= DB_LOGMAGIC
) {
399 __db_err(dblp
->dbenv
,
400 "Ignoring log file: %s: magic number %lx, not %lx",
401 fname
, (u_long
)persist
.magic
, (u_long
)DB_LOGMAGIC
);
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 fname
, (u_long
)persist
.version
);
414 * If we're going to use this log file, set the region's persistent
415 * information based on the headers.
418 dblp
->lp
->persist
.lg_max
= persist
.lg_max
;
419 dblp
->lp
->persist
.mode
= persist
.mode
;
423 err
: __os_freestr(fname
);
438 LOG_PANIC_CHECK(dblp
);
440 /* We may have opened files as part of XA; if so, close them. */
441 __log_close_files(dblp
);
443 /* Discard the per-thread pointer. */
444 if (dblp
->mutexp
!= NULL
) {
445 LOCK_LOGREGION(dblp
);
446 __db_shalloc_free(dblp
->addr
, dblp
->mutexp
);
447 UNLOCK_LOGREGION(dblp
);
450 /* Close the region. */
451 ret
= __db_rdetach(&dblp
->reginfo
);
453 /* Close open files, release allocated memory. */
454 if (dblp
->lfd
!= -1 && (t_ret
= __os_close(dblp
->lfd
)) != 0 && ret
== 0)
456 if (dblp
->c_dbt
.data
!= NULL
)
457 __os_free(dblp
->c_dbt
.data
, dblp
->c_dbt
.ulen
);
458 if (dblp
->c_fd
!= -1 &&
459 (t_ret
= __os_close(dblp
->c_fd
)) != 0 && ret
== 0)
461 if (dblp
->dbentry
!= NULL
) {
462 for (i
= 0; i
< dblp
->dbentry_cnt
; i
++)
463 if (dblp
->dbentry
[i
].name
!= NULL
)
464 __os_freestr(dblp
->dbentry
[i
].name
);
465 __os_free(dblp
->dbentry
,
466 (dblp
->dbentry_cnt
* sizeof(DB_ENTRY
)));
469 if (dblp
->dir
!= NULL
)
470 __os_freestr(dblp
->dir
);
472 if (dblp
->reginfo
.path
!= NULL
)
473 __os_freestr(dblp
->reginfo
.path
);
474 __os_free(dblp
, sizeof(*dblp
));
484 log_unlink(path
, force
, dbenv
)
492 memset(®info
, 0, sizeof(reginfo
));
493 reginfo
.dbenv
= dbenv
;
494 reginfo
.appname
= DB_APP_LOG
;
495 if (path
!= NULL
&& (ret
= __os_strdup(path
, ®info
.path
)) != 0)
497 reginfo
.file
= DB_DEFAULT_LOG_FILE
;
498 ret
= __db_runlink(®info
, force
);
499 if (reginfo
.path
!= NULL
)
500 __os_freestr(reginfo
.path
);
506 * Return LOG statistics.
509 log_stat(dblp
, gspp
, db_malloc
)
512 void *(*db_malloc
) __P((size_t));
520 LOG_PANIC_CHECK(dblp
);
522 if ((ret
= __os_malloc(sizeof(**gspp
), db_malloc
, gspp
)) != 0)
525 /* Copy out the global statistics. */
526 LOCK_LOGREGION(dblp
);
529 (*gspp
)->st_magic
= lp
->persist
.magic
;
530 (*gspp
)->st_version
= lp
->persist
.version
;
531 (*gspp
)->st_mode
= lp
->persist
.mode
;
532 (*gspp
)->st_lg_max
= lp
->persist
.lg_max
;
534 (*gspp
)->st_region_nowait
= lp
->rlayout
.lock
.mutex_set_nowait
;
535 (*gspp
)->st_region_wait
= lp
->rlayout
.lock
.mutex_set_wait
;
537 (*gspp
)->st_cur_file
= lp
->lsn
.file
;
538 (*gspp
)->st_cur_offset
= lp
->lsn
.offset
;
540 (*gspp
)->st_refcnt
= lp
->rlayout
.refcnt
;
541 (*gspp
)->st_regsize
= lp
->rlayout
.size
;
543 UNLOCK_LOGREGION(dblp
);