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