2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License, Version 1.0 only
2N/A * (the "License"). You may not use this file except in compliance
2N/A * with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * db_log.cc
2N/A *
2N/A * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A#include <stdio.h>
2N/A#include <errno.h>
2N/A
2N/A#include <malloc.h>
2N/A#include <string.h>
2N/A#ifdef TDRPC
2N/A#include <sysent.h>
2N/A#endif
2N/A#include <unistd.h>
2N/A
2N/A#include "db_headers.h"
2N/A#include "db_log.h"
2N/A
2N/A#include "nisdb_mt.h"
2N/A
2N/Astatic void
2N/Adelete_log_entry(db_log_entry *lentry)
2N/A{
2N/A db_query *q;
2N/A entry_object *obj;
2N/A if (lentry) {
2N/A if ((q = lentry->get_query())) {
2N/A delete q;
2N/A }
2N/A if ((obj = lentry->get_object())) {
2N/A free_entry(obj);
2N/A }
2N/A delete lentry;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Execute given function 'func' on log.
2N/A * function takes as arguments: pointer to log entry, character pointer to
2N/A * another argument, and pointer to an integer, which is used as a counter.
2N/A * 'func' should increment this value for each successful application.
2N/A * The log is traversed until either 'func' returns FALSE, or when the log
2N/A * is exhausted. The second argument to 'execute_on_log' is passed as the
2N/A * second argument to 'func'. The third argument, 'clean' determines whether
2N/A * the log entry is deleted after the function has been applied.
2N/A * Returns the number of times that 'func' incremented its third argument.
2N/A */
2N/Aint
2N/Adb_log::execute_on_log(bool_t (*func) (db_log_entry *, char *, int *),
2N/A char* arg, bool_t clean)
2N/A{
2N/A db_log_entry *j;
2N/A int count = 0;
2N/A bool_t done = FALSE;
2N/A
2N/A WRITELOCK(this, 0, "w db_log::execute_on_log");
2N/A if (open() == TRUE) { // open log
2N/A while (!done) {
2N/A j = get();
2N/A if (j == NULL)
2N/A break;
2N/A if ((*func)(j, arg, &count) == FALSE) done = TRUE;
2N/A if (clean) delete_log_entry(j);
2N/A }
2N/A
2N/A sync_log();
2N/A close();
2N/A }
2N/A WRITEUNLOCK(this, count, "wu db_log::execute_on_log");
2N/A
2N/A return (count);
2N/A}
2N/A
2N/Astatic bool_t
2N/Aprint_log_entry(db_log_entry *j, char * /* dummy */, int *count)
2N/A{
2N/A j->print();
2N/A ++ *count;
2N/A return (TRUE);
2N/A}
2N/A
2N/A/* Print contents of log file to stdout */
2N/Aint
2N/Adb_log::print()
2N/A{
2N/A return (execute_on_log(&(print_log_entry), NULL));
2N/A}
2N/A
2N/A/* Make copy of current log to log pointed to by 'f'. */
2N/Aint
2N/Adb_log::copy(db_log *f)
2N/A{
2N/A db_log_entry *j;
2N/A int l, ret = 0;
2N/A
2N/A WRITELOCK(f, -1, "w f db_log::copy");
2N/A if ((l = acqnonexcl()) != 0) {
2N/A WRITEUNLOCK(f, l, "wu f db_log::copy");
2N/A return (l);
2N/A }
2N/A for (;;) {
2N/A j = get();
2N/A if (j == NULL)
2N/A break;
2N/A if (f->append(j) < 0) {
2N/A WARNING_M(
2N/A "db_log::copy: could not append to log file: ");
2N/A ret = -1;
2N/A break;
2N/A }
2N/A delete_log_entry(j);
2N/A }
2N/A if ((l = relnonexcl()) != 0) {
2N/A ret = l;
2N/A }
2N/A WRITEUNLOCK(f, ret, "wu f db_log::copy");
2N/A return (ret);
2N/A}
2N/A
2N/A/* Rewinds current log */
2N/Aint
2N/Adb_log::rewind()
2N/A{
2N/A return (fseek(file, 0L, 0));
2N/A}
2N/A
2N/A/*
2N/A * Return the next element in current log; return NULL if end of log or error.
2N/A * Log must have been opened for READ.
2N/A */
2N/Adb_log_entry
2N/A*db_log::get()
2N/A{
2N/A db_log_entry *j;
2N/A
2N/A READLOCK(this, NULL, "r db_log::get");
2N/A if (mode != PICKLE_READ) {
2N/A READUNLOCK(this, NULL, "ru db_log::get");
2N/A return (NULL);
2N/A }
2N/A
2N/A j = new db_log_entry;
2N/A
2N/A if (j == NULL) {
2N/A READUNLOCK(this, NULL, "ru db_log::get");
2N/A return (NULL);
2N/A }
2N/A if (xdr_db_log_entry(&(xdr), j) == FALSE) {
2N/A delete_log_entry (j);
2N/A/* WARNING("Could not sucessfully finish reading log"); */
2N/A READUNLOCK(this, NULL, "ru db_log::get");
2N/A return (NULL);
2N/A }
2N/A if (! j->sane()) {
2N/A WARNING("truncated log entry found");
2N/A delete_log_entry(j);
2N/A j = NULL;
2N/A }
2N/A READUNLOCK(this, j, "ru db_log::get");
2N/A return (j);
2N/A}
2N/A
2N/A/* Append given log entry to log. */
2N/Aint
2N/Adb_log::append(db_log_entry *j)
2N/A{
2N/A int status;
2N/A
2N/A WRITELOCK(this, -1, "w db_log::append");
2N/A if (mode != PICKLE_APPEND) {
2N/A WRITEUNLOCK(this, -1, "wu db_log::append");
2N/A return (-1);
2N/A }
2N/A
2N/A /* xdr returns TRUE if successful, FALSE otherwise */
2N/A status = ((xdr_db_log_entry(&(xdr), j)) ? 0 : -1);
2N/A if (status < 0) {
2N/A WARNING("db_log: could not write log entry");
2N/A } else {
2N/A syncstate++;
2N/A }
2N/A WRITEUNLOCK(this, status, "wu db_log::append");
2N/A return (status);
2N/A}
2N/A
2N/Aint
2N/Acopy_log_file(char *oldname, char *newname) {
2N/A
2N/A int from, to, ret = 0;
2N/A ssize_t size, w, b;
2N/A char buf[8192];
2N/A
2N/A if ((from = open(oldname, O_RDONLY, 0666)) < 0) {
2N/A if (errno == ENOENT) {
2N/A return (0);
2N/A } else {
2N/A return (errno);
2N/A }
2N/A }
2N/A if ((to = open(newname, O_WRONLY|O_CREAT|O_TRUNC, 0660)) < 0) {
2N/A ret = errno;
2N/A (void) close(from);
2N/A return (ret);
2N/A }
2N/A
2N/A while ((size = read(from, buf, sizeof (buf))) > 0) {
2N/A b = 0;
2N/A while (size > 0) {
2N/A w = write(to, &buf[b], size);
2N/A if (w < 0) {
2N/A size == -1;
2N/A break;
2N/A }
2N/A size -= w;
2N/A b += w;
2N/A }
2N/A if (size != 0) {
2N/A ret = errno;
2N/A break;
2N/A }
2N/A }
2N/A
2N/A (void) close(from);
2N/A
2N/A if (ret != 0) {
2N/A errno = ret;
2N/A WARNING_M("db_log: error copying log file")
2N/A (void) close(to);
2N/A return (ret);
2N/A }
2N/A
2N/A if (fsync(to) != 0) {
2N/A ret = errno;
2N/A WARNING_M("db_log: error syncing log file");
2N/A }
2N/A
2N/A (void) close(to);
2N/A
2N/A return (ret);
2N/A
2N/A}
2N/A
2N/A/*
2N/A * Return value is expected to be the usual C convention of non-zero
2N/A * for success, 0 for failure.
2N/A */
2N/Aint
2N/Adb_log::sync_log()
2N/A{
2N/A int status, err;
2N/A
2N/A WRITELOCK(this, -1, "w db_log::sync_log");
2N/A status = fflush(file);
2N/A if (status < 0) {
2N/A WARNING("db_log: could not flush log entry to disk");
2N/A WRITEUNLOCK(this, status, "wu db_log::sync_log");
2N/A return (status);
2N/A }
2N/A
2N/A status = fsync(fileno(file));
2N/A if (status < 0) {
2N/A WARNING("db_log: could not sync log entry to disk");
2N/A } else if (tmplog != 0) {
2N/A if (syncstate == 0) {
2N/A /* Log already stable; nothing to do */
2N/A err = 0;
2N/A } else if ((err = copy_log_file(tmplog, stablelog)) == 0) {
2N/A if (rename(stablelog, oldlog) != 0) {
2N/A WARNING_M("db_log: could not mv stable log");
2N/A } else {
2N/A syncstate = 0;
2N/A }
2N/A } else {
2N/A errno = err;
2N/A WARNING_M("db_log: could not stabilize log");
2N/A }
2N/A status = (err == 0);
2N/A } else {
2N/A /*
2N/A * Successful sync of file, but no tmplog to sync
2N/A * so we make sure we return 'success'.
2N/A */
2N/A status = 1;
2N/A }
2N/A WRITEUNLOCK(this, status, "wu db_log::sync_log");
2N/A return (status);
2N/A}
2N/A
2N/Aint
2N/Adb_log::close() {
2N/A
2N/A int ret;
2N/A
2N/A WRITELOCK(this, -1, "w db_log::close");
2N/A if (mode != PICKLE_READ && oldlog != 0) {
2N/A if (syncstate != 0) {
2N/A WARNING("db_log: closing unstable tmp log");
2N/A }
2N/A filename = oldlog;
2N/A oldlog = 0;
2N/A }
2N/A
2N/A ret = pickle_file::close();
2N/A if (tmplog != 0) {
2N/A (void) unlink(tmplog);
2N/A delete tmplog;
2N/A tmplog = 0;
2N/A }
2N/A if (stablelog != 0) {
2N/A delete stablelog;
2N/A stablelog = 0;
2N/A }
2N/A WRITEUNLOCK(this, ret, "wu db_log::close");
2N/A return (ret);
2N/A}
2N/A
2N/Abool_t
2N/Adb_log::open(void) {
2N/A
2N/A int len, cpstat;
2N/A bool_t ret;
2N/A
2N/A WRITELOCK(this, FALSE, "w db_log::open");
2N/A if (mode == PICKLE_READ || (!copylog)) {
2N/A ret = pickle_file::open();
2N/A WRITEUNLOCK(this, ret, "wu db_log::open");
2N/A return (ret);
2N/A }
2N/A
2N/A len = strlen(filename);
2N/A tmplog = new char[len + sizeof (".tmp")];
2N/A if (tmplog == 0) {
2N/A WARNING("db_log: could not allocate tmp log name");
2N/A ret = pickle_file::open();
2N/A WRITEUNLOCK(this, ret, "wu db_log::open");
2N/A return (ret);
2N/A }
2N/A stablelog = new char[len + sizeof (".stable")];
2N/A if (stablelog == 0) {
2N/A WARNING("db_log: could not allocate stable log name");
2N/A delete tmplog;
2N/A tmplog = 0;
2N/A ret = pickle_file::open();
2N/A WRITEUNLOCK(this, ret, "wu db_log::open");
2N/A return (ret);
2N/A }
2N/A sprintf(tmplog, "%s.tmp", filename);
2N/A sprintf(stablelog, "%s.stable", filename);
2N/A
2N/A if ((cpstat = copy_log_file(filename, tmplog)) == 0) {
2N/A oldlog = filename;
2N/A filename = tmplog;
2N/A } else {
2N/A syslog(LOG_WARNING,
2N/A "db_log: Error copying \"%s\" to \"%s\": %s",
2N/A filename, tmplog, strerror(cpstat));
2N/A delete tmplog;
2N/A tmplog = 0;
2N/A delete stablelog;
2N/A stablelog = 0;
2N/A }
2N/A
2N/A ret = pickle_file::open();
2N/A WRITEUNLOCK(this, ret, "wu db_log::open");
2N/A return (ret);
2N/A}