Home | History | Annotate | Download | only in libnisdb
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  *	db_log.cc
     24  *
     25  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
     26  * Use is subject to license terms.
     27  */
     28 
     29 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     30 
     31 #include <stdio.h>
     32 #include <errno.h>
     33 
     34 #include <malloc.h>
     35 #include <string.h>
     36 #ifdef TDRPC
     37 #include <sysent.h>
     38 #endif
     39 #include <unistd.h>
     40 
     41 #include "db_headers.h"
     42 #include "db_log.h"
     43 
     44 #include "nisdb_mt.h"
     45 
     46 static void
     47 delete_log_entry(db_log_entry *lentry)
     48 {
     49 	db_query *q;
     50 	entry_object *obj;
     51 	if (lentry) {
     52 		if ((q = lentry->get_query())) {
     53 			delete q;
     54 		}
     55 		if ((obj = lentry->get_object())) {
     56 			free_entry(obj);
     57 		}
     58 		delete lentry;
     59 	}
     60 }
     61 
     62 /*
     63  * Execute given function 'func' on log.
     64  * function takes as arguments: pointer to log entry, character pointer to
     65  * another argument, and pointer to an integer, which is used as a counter.
     66  * 'func' should increment this value for each successful application.
     67  * The log is traversed until either 'func' returns FALSE, or when the log
     68  * is exhausted.  The second argument to 'execute_on_log' is passed as the
     69  * second argument to 'func'.  The third argument, 'clean' determines whether
     70  * the log entry is deleted after the function has been applied.
     71  * Returns the number of times that 'func' incremented its third argument.
     72  */
     73 int
     74 db_log::execute_on_log(bool_t (*func) (db_log_entry *, char *, int *),
     75 			    char* arg, bool_t clean)
     76 {
     77 	db_log_entry    *j;
     78 	int count = 0;
     79 	bool_t done = FALSE;
     80 
     81 	WRITELOCK(this, 0, "w db_log::execute_on_log");
     82 	if (open() == TRUE) {   // open log
     83 		while (!done) {
     84 			j = get();
     85 			if (j == NULL)
     86 				break;
     87 			if ((*func)(j, arg, &count) == FALSE) done = TRUE;
     88 			if (clean) delete_log_entry(j);
     89 		}
     90 
     91 		sync_log();
     92 		close();
     93 	}
     94 	WRITEUNLOCK(this, count, "wu db_log::execute_on_log");
     95 
     96 	return (count);
     97 }
     98 
     99 static bool_t
    100 print_log_entry(db_log_entry *j, char * /* dummy */, int *count)
    101 {
    102 	j->print();
    103 	++ *count;
    104 	return (TRUE);
    105 }
    106 
    107 /* Print contents of log file to stdout */
    108 int
    109 db_log::print()
    110 {
    111 	return (execute_on_log(&(print_log_entry), NULL));
    112 }
    113 
    114 /* Make copy of current log to log pointed to by 'f'. */
    115 int
    116 db_log::copy(db_log *f)
    117 {
    118 	db_log_entry *j;
    119 	int	l, ret = 0;
    120 
    121 	WRITELOCK(f, -1, "w f db_log::copy");
    122 	if ((l = acqnonexcl()) != 0) {
    123 		WRITEUNLOCK(f, l, "wu f db_log::copy");
    124 		return (l);
    125 	}
    126 	for (;;) {
    127 		j = get();
    128 		if (j == NULL)
    129 			break;
    130 		if (f->append(j) < 0) {
    131 			WARNING_M(
    132 			"db_log::copy: could not append to log file: ");
    133 			ret = -1;
    134 			break;
    135 		}
    136 		delete_log_entry(j);
    137 	}
    138 	if ((l = relnonexcl()) != 0) {
    139 		ret = l;
    140 	}
    141 	WRITEUNLOCK(f, ret, "wu f db_log::copy");
    142 	return (ret);
    143 }
    144 
    145 /* Rewinds current log */
    146 int
    147 db_log::rewind()
    148 {
    149 	return (fseek(file, 0L, 0));
    150 }
    151 
    152 /*
    153  * Return the next element in current log; return NULL if end of log or error.
    154  * Log must have been opened for READ.
    155  */
    156 db_log_entry
    157 *db_log::get()
    158 {
    159 	db_log_entry *j;
    160 
    161 	READLOCK(this, NULL, "r db_log::get");
    162 	if (mode != PICKLE_READ) {
    163 		READUNLOCK(this, NULL, "ru db_log::get");
    164 		return (NULL);
    165 	}
    166 
    167 	j = new db_log_entry;
    168 
    169 	if (j == NULL) {
    170 		READUNLOCK(this, NULL, "ru db_log::get");
    171 		return (NULL);
    172 	}
    173 	if (xdr_db_log_entry(&(xdr), j) == FALSE) {
    174 		delete_log_entry (j);
    175 /*    WARNING("Could not sucessfully finish reading log"); */
    176 		READUNLOCK(this, NULL, "ru db_log::get");
    177 		return (NULL);
    178 	}
    179 	if (! j->sane()) {
    180 		WARNING("truncated log entry found");
    181 		delete_log_entry(j);
    182 		j = NULL;
    183 	}
    184 	READUNLOCK(this, j, "ru db_log::get");
    185 	return (j);
    186 }
    187 
    188 /* Append given log entry to log. */
    189 int
    190 db_log::append(db_log_entry *j)
    191 {
    192 	int status;
    193 
    194 	WRITELOCK(this, -1, "w db_log::append");
    195 	if (mode != PICKLE_APPEND) {
    196 		WRITEUNLOCK(this, -1, "wu db_log::append");
    197 		return (-1);
    198 	}
    199 
    200 	/* xdr returns TRUE if successful, FALSE otherwise */
    201 	status = ((xdr_db_log_entry(&(xdr), j)) ? 0 : -1);
    202 	if (status < 0) {
    203 		WARNING("db_log: could not write log entry");
    204 	} else {
    205 		syncstate++;
    206 	}
    207 	WRITEUNLOCK(this, status, "wu db_log::append");
    208 	return (status);
    209 }
    210 
    211 int
    212 copy_log_file(char *oldname, char *newname) {
    213 
    214 	int	from, to, ret = 0;
    215 	ssize_t	size, w, b;
    216 	char	buf[8192];
    217 
    218 	if ((from = open(oldname, O_RDONLY, 0666)) < 0) {
    219 		if (errno == ENOENT) {
    220 			return (0);
    221 		} else {
    222 			return (errno);
    223 		}
    224 	}
    225 	if ((to = open(newname, O_WRONLY|O_CREAT|O_TRUNC, 0660)) < 0) {
    226 		ret = errno;
    227 		(void) close(from);
    228 		return (ret);
    229 	}
    230 
    231 	while ((size = read(from, buf, sizeof (buf))) > 0) {
    232 		b = 0;
    233 		while (size > 0) {
    234 			w = write(to, &buf[b], size);
    235 			if (w < 0) {
    236 				size == -1;
    237 				break;
    238 			}
    239 			size -= w;
    240 			b += w;
    241 		}
    242 		if (size != 0) {
    243 			ret = errno;
    244 			break;
    245 		}
    246 	}
    247 
    248 	(void) close(from);
    249 
    250 	if (ret != 0) {
    251 		errno = ret;
    252 		WARNING_M("db_log: error copying log file")
    253 		(void) close(to);
    254 		return (ret);
    255 	}
    256 
    257 	if (fsync(to) != 0) {
    258 		ret = errno;
    259 		WARNING_M("db_log: error syncing log file");
    260 	}
    261 
    262 	(void) close(to);
    263 
    264 	return (ret);
    265 
    266 }
    267 
    268 /*
    269  * Return value is expected to be the usual C convention of non-zero
    270  * for success, 0 for failure.
    271  */
    272 int
    273 db_log::sync_log()
    274 {
    275 	int status, err;
    276 
    277 	WRITELOCK(this, -1, "w db_log::sync_log");
    278 	status = fflush(file);
    279 	if (status < 0) {
    280 		WARNING("db_log: could not flush log entry to disk");
    281 		WRITEUNLOCK(this, status, "wu db_log::sync_log");
    282 		return (status);
    283 	}
    284 
    285 	status = fsync(fileno(file));
    286 	if (status < 0) {
    287 		WARNING("db_log: could not sync log entry to disk");
    288 	} else if (tmplog != 0) {
    289 		if (syncstate == 0) {
    290 			/* Log already stable; nothing to do */
    291 			err = 0;
    292 		} else if ((err = copy_log_file(tmplog, stablelog)) == 0) {
    293 			if (rename(stablelog, oldlog) != 0) {
    294 				WARNING_M("db_log: could not mv stable log");
    295 			} else {
    296 				syncstate = 0;
    297 			}
    298 		} else {
    299 			errno = err;
    300 			WARNING_M("db_log: could not stabilize log");
    301 		}
    302 		status = (err == 0);
    303 	} else {
    304 		/*
    305 		 * Successful sync of file, but no tmplog to sync
    306 		 * so we make sure we return 'success'.
    307 		 */
    308 		status = 1;
    309 	}
    310 	WRITEUNLOCK(this, status, "wu db_log::sync_log");
    311 	return (status);
    312 }
    313 
    314 int
    315 db_log::close() {
    316 
    317 	int ret;
    318 
    319 	WRITELOCK(this, -1, "w db_log::close");
    320 	if (mode != PICKLE_READ && oldlog != 0) {
    321 		if (syncstate != 0) {
    322 			WARNING("db_log: closing unstable tmp log");
    323 		}
    324 		filename = oldlog;
    325 		oldlog = 0;
    326 	}
    327 
    328 	ret = pickle_file::close();
    329 	if (tmplog != 0) {
    330 		(void) unlink(tmplog);
    331 		delete tmplog;
    332 		tmplog = 0;
    333 	}
    334 	if (stablelog != 0) {
    335 		delete stablelog;
    336 		stablelog = 0;
    337 	}
    338 	WRITEUNLOCK(this, ret, "wu db_log::close");
    339 	return (ret);
    340 }
    341 
    342 bool_t
    343 db_log::open(void) {
    344 
    345 	int	len, cpstat;
    346 	bool_t	ret;
    347 
    348 	WRITELOCK(this, FALSE, "w db_log::open");
    349 	if (mode == PICKLE_READ || (!copylog)) {
    350 		ret = pickle_file::open();
    351 		WRITEUNLOCK(this, ret, "wu db_log::open");
    352 		return (ret);
    353 	}
    354 
    355 	len = strlen(filename);
    356 	tmplog = new char[len + sizeof (".tmp")];
    357 	if (tmplog == 0) {
    358 		WARNING("db_log: could not allocate tmp log name");
    359 		ret = pickle_file::open();
    360 		WRITEUNLOCK(this, ret, "wu db_log::open");
    361 		return (ret);
    362 	}
    363 	stablelog = new char[len + sizeof (".stable")];
    364 	if (stablelog == 0) {
    365 		WARNING("db_log: could not allocate stable log name");
    366 		delete tmplog;
    367 		tmplog = 0;
    368 		ret = pickle_file::open();
    369 		WRITEUNLOCK(this, ret, "wu db_log::open");
    370 		return (ret);
    371 	}
    372 	sprintf(tmplog, "%s.tmp", filename);
    373 	sprintf(stablelog, "%s.stable", filename);
    374 
    375 	if ((cpstat = copy_log_file(filename, tmplog)) == 0) {
    376 		oldlog = filename;
    377 		filename = tmplog;
    378 	} else {
    379 		syslog(LOG_WARNING,
    380 			"db_log: Error copying \"%s\" to \"%s\": %s",
    381 			filename, tmplog, strerror(cpstat));
    382 		delete tmplog;
    383 		tmplog = 0;
    384 		delete stablelog;
    385 		stablelog = 0;
    386 	}
    387 
    388 	ret = pickle_file::open();
    389 	WRITEUNLOCK(this, ret, "wu db_log::open");
    390 	return (ret);
    391 }
    392