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