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
delete_log_entry(db_log_entry * lentry)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
execute_on_log(bool_t (* func)(db_log_entry *,char *,int *),char * arg,bool_t clean)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
print_log_entry(db_log_entry * j,char *,int * count)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
print()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
copy(db_log * f)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
rewind()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
get()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
append(db_log_entry * j)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
copy_log_file(char * oldname,char * newname)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
sync_log()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
close()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
open(void)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