xref: /illumos-gate/usr/src/lib/libnisdb/db_log.cc (revision 7c478bd9)
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