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