1/*- 2 * Copyright (c) 2004-2009 Oracle. All rights reserved. 3 * 4 * http://www.apache.org/licenses/LICENSE-2.0.txt 5 * 6 * authors: George Schlossnagle <george@omniti.com> 7 */ 8 9extern "C" 10{ 11#include "httpd.h" 12#include "http_config.h" 13#include "http_core.h" /* For REMOTE_NAME */ 14#include "http_log.h" 15 16#include "sem_utils.h" 17#include "skiplist.h" 18#include "mm_hash.h" 19} 20 21#include "utils.h" 22#include "db_cxx.h" 23 24/* the semaphore set for the application */ 25static int semset; 26 27/* process-local handle for global ref count management */ 28/* individual semaphores */ 29#define OK_TO_PROCEED 0 30#define GLOBAL_LOCK 1 31#define NUM_SEMS 2 32 33/* mm helpers */ 34static MM *mm; 35static MM_Hash *ref_counts; 36 37/* locks */ 38int env_locks_init() 39{ 40 char shmpath[32]; 41 unsigned short start[2] = { 1, 1 }; 42 43 snprintf(shmpath, 32, "/tmp/.mod_db4.%d", getpid()); 44 mm = mm_create(0, shmpath); 45 if(NULL == mm) { 46 return -1; 47 } 48 mm_lock(mm, MM_LOCK_RW); 49 ref_counts = mm_hash_new(mm, NULL); 50 mm_unlock(mm); 51 if (!ref_counts) { 52 return -1; 53 } 54 if((semset = md4_sem_create(NUM_SEMS, start)) < 0) { 55 return -1; 56 } 57 return 0; 58} 59 60void env_global_rw_lock() 61{ 62 mm_lock(mm, MM_LOCK_RW); 63} 64 65void env_global_rd_lock() 66{ 67 mm_lock(mm, MM_LOCK_RD); 68} 69 70void env_global_unlock() 71{ 72 mm_unlock(mm); 73} 74 75void env_wait_for_child_crash() 76{ 77 md4_sem_wait_for_zero(semset, OK_TO_PROCEED); 78} 79 80void env_child_crash() 81{ 82 md4_sem_set(semset, OK_TO_PROCEED, 0); 83} 84 85void env_ok_to_proceed() 86{ 87 md4_sem_set(semset, OK_TO_PROCEED, 1); 88} 89 90/* process resource globals */ 91static Skiplist open_transactions; 92static Skiplist open_cursors; 93static Skiplist open_log_cursors; 94static Skiplist open_dbs; 95static Skiplist open_dbenvs; 96 97/* named pointers for db_env and bd skiplists */ 98struct named_resource { 99 char *name; 100 void *ptr; 101}; 102 103/* skiplist comparitors for void pointers */ 104 105static int VP_COMPARE(void *a, void *b) 106{ 107 return (a < b) ? (-1) : ((a == b) ? (0) : (1)); 108} 109 110/* key for comparing DB *'s in skiplist */ 111 112struct db_key { 113 const char *fname; 114 const char *dname; 115}; 116 117static int DB_COMPARE(void *a, void *b) 118{ 119 int ret; 120 DB *ae = (DB *) a; 121 DB *be = (DB *) b; 122 if(ae->fname == NULL) { 123 if(be->fname == NULL) { 124 return (ae < be) ? (-1) : ((ae == be) ? (0) : (1)); 125 } 126 return 1; 127 } 128 else if(be->fname == NULL) { 129 /* ae->fname != NULL, from above */ 130 return -1; 131 } 132 ret = strcmp(ae->fname, be->fname); 133 if(ret == 0) { 134 if(ae->dname == NULL) { 135 if(be->dname == NULL) { 136 return 0; 137 } 138 return 1; 139 } 140 else if(be->dname == NULL) { 141 return -1; 142 } 143 ret = strcmp(ae->dname, be->dname); 144 } 145 return ret; 146} 147 148static int DB_COMPARE_K(void *a, void *b) 149{ 150 struct db_key *akey = (struct db_key *) a; 151 DB *be = (DB *) b; 152 int ret; 153 if(akey->fname == NULL) { 154 if(be->fname == NULL) { 155 /* should never match here */ 156 return (a < b) ? (-1) : ((a == b) ? (0) : (1)); 157 } 158 return 1; 159 } 160 else if(be->fname == NULL) { 161 /* akey->fname != NULL, from above */ 162 return -1; 163 } 164 ret = strcmp(akey->fname, be->fname); 165 if(ret == 0) { 166 if(akey->dname == NULL) { 167 if(be->dname == NULL) { 168 return 0; 169 } 170 return 1; 171 } 172 else if(be->dname == NULL) { 173 return -1; 174 } 175 ret = strcmp(akey->dname, be->dname); 176 } 177 return ret; 178} 179 180static int DBENV_COMPARE(void *a, void *b) 181{ 182 DB_ENV *ae = (DB_ENV *) a; 183 DB_ENV *be = (DB_ENV *) b; 184 return strcmp(ae->db_home, be->db_home); 185} 186 187static int DBENV_COMPARE_K(void *a, void *b) 188{ 189 const char *aname = (const char *) a; 190 DB_ENV *be = (DB_ENV *) b; 191 return strcmp(aname, be->db_home); 192} 193 194void env_rsrc_list_init() 195{ 196 skiplist_init(&open_transactions); 197 skiplist_set_compare(&open_transactions, VP_COMPARE, VP_COMPARE); 198 199 skiplist_init(&open_cursors); 200 skiplist_set_compare(&open_cursors, VP_COMPARE, VP_COMPARE); 201 202 skiplist_init(&open_log_cursors); 203 skiplist_set_compare(&open_log_cursors, VP_COMPARE, VP_COMPARE); 204 205 skiplist_init(&open_dbs); 206 skiplist_set_compare(&open_dbs, DB_COMPARE, DB_COMPARE_K); 207 208 skiplist_init(&open_dbenvs); 209 skiplist_set_compare(&open_dbenvs, DBENV_COMPARE, DBENV_COMPARE_K); 210} 211 212static void register_cursor(DBC *dbc) 213{ 214 skiplist_insert(&open_cursors, dbc); 215} 216 217static void unregister_cursor(DBC *dbc) 218{ 219 skiplist_remove(&open_cursors, dbc, NULL); 220} 221 222static void register_log_cursor(DB_LOGC *cursor) 223{ 224 skiplist_insert(&open_log_cursors, cursor); 225} 226 227static void unregister_log_cursor(DB_LOGC *cursor) 228{ 229 skiplist_remove(&open_log_cursors, cursor, NULL); 230} 231 232static void register_transaction(DB_TXN *txn) 233{ 234 skiplist_insert(&open_transactions, txn); 235} 236 237static void unregister_transaction(DB_TXN *txn) 238{ 239 skiplist_remove(&open_transactions, txn, NULL); 240} 241 242static DB *retrieve_db(const char *fname, const char *dname) 243{ 244 DB *rv; 245 struct db_key key; 246 if(fname == NULL) { 247 return NULL; 248 } 249 key.fname = fname; 250 key.dname = dname; 251 rv = (DB *) skiplist_find(&open_dbs, (void *) &key, NULL); 252 return rv; 253} 254 255static void register_db(DB *db) 256{ 257 skiplist_insert(&open_dbs, db); 258} 259 260static void unregister_db(DB *db) 261{ 262 struct db_key key; 263 key.fname = db->fname; 264 key.dname = db->dname; 265 skiplist_remove(&open_dbs, &key, NULL); 266} 267 268static DB_ENV *retrieve_db_env(const char *db_home) 269{ 270 return (DB_ENV *) skiplist_find(&open_dbenvs, (void *) db_home, NULL); 271} 272 273static void register_db_env(DB_ENV *dbenv) 274{ 275 global_ref_count_increase(dbenv->db_home); 276 skiplist_insert(&open_dbenvs, dbenv); 277} 278 279static void unregister_db_env(DB_ENV *dbenv) 280{ 281 global_ref_count_decrease(dbenv->db_home); 282 skiplist_remove(&open_dbenvs, dbenv->db_home, NULL); 283} 284 285int global_ref_count_increase(char *path) 286{ 287 int refcount = 0; 288 int pathlen = 0; 289 pathlen = strlen(path); 290 291 env_global_rw_lock(); 292 refcount = (int) mm_hash_find(ref_counts, path, pathlen); 293 refcount++; 294 mm_hash_update(ref_counts, path, pathlen, (void *)refcount); 295 env_global_unlock(); 296 return refcount; 297} 298 299int global_ref_count_decrease(char *path) 300{ 301 int refcount = 0; 302 int pathlen = 0; 303 pathlen = strlen(path); 304 305 env_global_rw_lock(); 306 refcount = (int) mm_hash_find(ref_counts, path, pathlen); 307 if(refcount > 0) refcount--; 308 mm_hash_update(ref_counts, path, pathlen, (void *)refcount); 309 env_global_unlock(); 310 return refcount; 311} 312 313int global_ref_count_get(const char *path) 314{ 315 int refcount = 0; 316 int pathlen = 0; 317 pathlen = strlen(path); 318 319 env_global_rd_lock(); 320 refcount = (int) mm_hash_find(ref_counts, path, pathlen); 321 env_global_unlock(); 322 return refcount; 323} 324 325void global_ref_count_clean() 326{ 327 env_global_rd_lock(); 328 mm_hash_free(ref_counts); 329 ref_counts = mm_hash_new(mm, NULL); 330 env_global_unlock(); 331} 332 333/* wrapper methods {{{ */ 334 335static int (*old_log_cursor_close)(DB_LOGC *, u_int32_t) = NULL; 336static int new_log_cursor_close(DB_LOGC *cursor, u_int32_t flags) 337{ 338 unregister_log_cursor(cursor); 339 return old_log_cursor_close(cursor, flags); 340} 341 342static int (*old_db_txn_abort)(DB_TXN *) = NULL; 343static int new_db_txn_abort(DB_TXN *tid) 344{ 345 unregister_transaction(tid); 346 return old_db_txn_abort(tid); 347} 348 349static int (*old_db_txn_commit)(DB_TXN *, u_int32_t) = NULL; 350static int new_db_txn_commit(DB_TXN *tid, u_int32_t flags) 351{ 352 unregister_transaction(tid); 353 return old_db_txn_commit(tid, flags); 354} 355 356static int (*old_db_txn_discard)(DB_TXN *, u_int32_t) = NULL; 357static int new_db_txn_discard(DB_TXN *tid, u_int32_t flags) 358{ 359 unregister_transaction(tid); 360 return old_db_txn_discard(tid, flags); 361} 362 363static int (*old_db_env_txn_begin)(DB_ENV *, DB_TXN *, DB_TXN **, u_int32_t); 364static int new_db_env_txn_begin(DB_ENV *env, DB_TXN *parent, DB_TXN **tid, u_int32_t flags) 365{ 366 int ret; 367 if((ret = old_db_env_txn_begin(env, parent, tid, flags)) == 0) { 368 register_transaction(*tid); 369 /* overload DB_TXN->abort */ 370 if(old_db_txn_abort == NULL) { 371 old_db_txn_abort = (*tid)->abort; 372 } 373 (*tid)->abort = new_db_txn_abort; 374 375 /* overload DB_TXN->commit */ 376 if(old_db_txn_commit == NULL) { 377 old_db_txn_commit = (*tid)->commit; 378 } 379 (*tid)->commit = new_db_txn_commit; 380 381 /* overload DB_TXN->discard */ 382 if(old_db_txn_discard == NULL) { 383 old_db_txn_discard = (*tid)->discard; 384 } 385 (*tid)->discard = new_db_txn_discard; 386 } 387 return ret; 388} 389 390static int (*old_db_env_open)(DB_ENV *, const char *, u_int32_t, int) = NULL; 391static int new_db_env_open(DB_ENV *dbenv, const char *db_home, u_int32_t flags, int mode) 392{ 393 int ret =666; 394 DB_ENV *cached_dbenv; 395 flags |= DB_INIT_MPOOL; 396 /* if global ref count is 0, open for recovery */ 397 if(global_ref_count_get(db_home) == 0) { 398 flags |= DB_RECOVER; 399 flags |= DB_INIT_TXN; 400 flags |= DB_CREATE; 401 } 402 if((cached_dbenv = retrieve_db_env(db_home)) != NULL) { 403 memcpy(dbenv, cached_dbenv, sizeof(DB_ENV)); 404 ret = 0; 405 } 406 else if((ret = old_db_env_open(dbenv, db_home, flags, mode)) == 0) { 407 register_db_env(dbenv); 408 } 409 return ret; 410} 411 412static int(*old_db_env_close)(DB_ENV *, u_int32_t) = NULL; 413static int new_db_env_close(DB_ENV *dbenv, u_int32_t flags) 414{ 415 int ret; 416 /* we're already locked */ 417 unregister_db_env(dbenv); 418 ret = old_db_env_close(dbenv, flags); 419} 420 421static int (*old_db_env_log_cursor)(DB_ENV *, DB_LOGC **, u_int32_t) = NULL; 422static int new_db_env_log_cursor(DB_ENV *dbenv, DB_LOGC **cursop, u_int32_t flags) 423{ 424 int ret; 425 if((ret = old_db_env_log_cursor(dbenv, cursop, flags)) == 0) { 426 register_log_cursor(*cursop); 427 if(old_log_cursor_close == NULL) { 428 old_log_cursor_close = (*cursop)->close; 429 } 430 (*cursop)->close = new_log_cursor_close; 431 } 432 return ret; 433} 434 435static int (*old_db_open)(DB *, DB_TXN *, const char *, const char *, DBTYPE, u_int32_t, int) = NULL; 436static int new_db_open(DB *db, DB_TXN *txnid, const char *file, 437 const char *database, DBTYPE type, u_int32_t flags, int mode) 438{ 439 int ret; 440 DB *cached_db; 441 442 cached_db = retrieve_db(file, database); 443 if(cached_db) { 444 memcpy(db, cached_db, sizeof(DB)); 445 ret = 0; 446 } 447 else if((ret = old_db_open(db, txnid, file, database, type, flags, mode)) == 0) { 448 register_db(db); 449 } 450 return ret; 451} 452 453static int (*old_db_close)(DB *, u_int32_t) = NULL; 454static int new_db_close(DB *db, u_int32_t flags) 455{ 456 unregister_db(db); 457 return old_db_close(db, flags); 458} 459 460 461static int (*old_dbc_close)(DBC *); 462static int new_dbc_close(DBC *cursor) 463{ 464 unregister_cursor(cursor); 465 return old_dbc_close(cursor); 466} 467 468static int (*old_dbc_dup)(DBC *, DBC **, u_int32_t) = NULL; 469static int new_dbc_dup(DBC *oldcursor, DBC **newcursor, u_int32_t flags) 470{ 471 int ret; 472 if((ret = old_dbc_dup(oldcursor, newcursor, flags)) == 0) { 473 register_cursor(*newcursor); 474 475 /* overload DBC->close */ 476 (*newcursor)->close = oldcursor->close; 477 478 /* overload DBC->dup */ 479 (*newcursor)->dup = oldcursor->dup; 480 } 481 return ret; 482} 483 484static int (*old_db_cursor)(DB *, DB_TXN *, DBC **, u_int32_t) = NULL; 485static int new_db_cursor(DB *db, DB_TXN *txnid, DBC **cursop, u_int32_t flags) 486{ 487 int ret; 488 if((ret = old_db_cursor(db, txnid, cursop, flags)) == 0) { 489 register_cursor(*cursop); 490 491 /* overload DBC->close */ 492 if(old_dbc_close == NULL) { 493 old_dbc_close = (*cursop)->close; 494 } 495 (*cursop)->close = new_dbc_close; 496 497 /* overload DBC->dup */ 498 if(old_dbc_dup == NULL) { 499 old_dbc_dup = (*cursop)->dup; 500 } 501 (*cursop)->dup = new_dbc_dup; 502 } 503 return ret; 504} 505 506/* }}} */ 507 508/* {{{ new DB_ENV constructor 509 */ 510 511int mod_db4_db_env_create(DB_ENV **dbenvp, u_int32_t flags) 512{ 513 int cachesize = 0; 514 int ret; 515 DB_ENV *dbenv; 516 517 if ((ret = db_env_create(dbenvp, 0)) != 0) { 518 /* FIXME report error */ 519 520 return ret; 521 } 522 dbenv = *dbenvp; 523 DbEnv::wrap_DB_ENV(dbenv); 524 /* Here we set defaults settings for the db_env */ 525 /* grab context info from httpd.conf for error file */ 526 /* grab context info for cachesize */ 527 if (0 && cachesize) { 528 if ((ret = dbenv->set_cachesize(dbenv, 0, cachesize, 0)) != 0) { 529 dbenv->err(dbenv, ret, "set_cachesize"); 530 dbenv->close(dbenv, 0); 531 } 532 } 533 /* overload DB_ENV->open */ 534 if(old_db_env_open == NULL) { 535 old_db_env_open = dbenv->open; 536 } 537 dbenv->open = new_db_env_open; 538 539 /* overload DB_ENV->close */ 540 if(old_db_env_close == NULL) { 541 old_db_env_close = dbenv->close; 542 } 543 dbenv->close = new_db_env_close; 544 545 /* overload DB_ENV->log_cursor */ 546 if(old_db_env_log_cursor == NULL) { 547 old_db_env_log_cursor = dbenv->log_cursor; 548 } 549 dbenv->log_cursor = new_db_env_log_cursor; 550 551 /* overload DB_ENV->txn_begin */ 552 if(old_db_env_txn_begin == NULL) { 553 old_db_env_txn_begin = dbenv->txn_begin; 554 } 555 dbenv->txn_begin = new_db_env_txn_begin; 556 return 0; 557} 558/* }}} */ 559 560/* {{{ new DB constructor 561 */ 562int mod_db4_db_create(DB **dbp, DB_ENV *dbenv, u_int32_t flags) 563{ 564 int ret; 565 566flags = 0; 567 568 if((ret = db_create(dbp, dbenv, flags)) == 0) { 569 /* FIXME this should be removed I think register_db(*dbp); */ 570 /* overload DB->open */ 571 if(old_db_open == NULL) { 572 old_db_open = (*dbp)->open; 573 } 574 (*dbp)->open = new_db_open; 575 576 /* overload DB->close */ 577 if(old_db_close == NULL) { 578 old_db_close = (*dbp)->close; 579 } 580 (*dbp)->close = new_db_close; 581 582 /* overload DB->cursor */ 583 if(old_db_cursor == NULL) { 584 old_db_cursor = (*dbp)->cursor; 585 } 586 (*dbp)->cursor = new_db_cursor; 587 } 588 return ret; 589} 590 591/* }}} */ 592 593void mod_db4_child_clean_request_shutdown() 594{ 595 DBC *cursor; 596 DB_TXN *transaction; 597 while(cursor = (DBC *)skiplist_pop(&open_cursors, NULL)) { 598 cursor->close(cursor); 599 } 600 while(transaction = (DB_TXN *)skiplist_pop(&open_transactions, NULL)) { 601 transaction->abort(transaction); 602 } 603} 604 605void mod_db4_child_clean_process_shutdown() 606{ 607 DB *db; 608 DB_ENV *dbenv; 609 mod_db4_child_clean_request_shutdown(); 610 while(db = (DB *)skiplist_pop(&open_dbs, NULL)) { 611 db->close(db, 0); 612 } 613 while(dbenv = (DB_ENV *)skiplist_pop(&open_dbenvs, NULL)) { 614 DbEnv *dbe = DbEnv::get_DbEnv(dbenv); 615 global_ref_count_decrease(dbenv->db_home); 616 dbe->close(0); 617 delete dbe; 618 } 619} 620/* vim: set ts=4 sts=4 expandtab bs=2 ai fdm=marker: */ 621