1251876Speter/* Licensed to the Apache Software Foundation (ASF) under one or more 2251876Speter * contributor license agreements. See the NOTICE file distributed with 3251876Speter * this work for additional information regarding copyright ownership. 4251876Speter * The ASF licenses this file to You under the Apache License, Version 2.0 5251876Speter * (the "License"); you may not use this file except in compliance with 6251876Speter * the License. You may obtain a copy of the License at 7251876Speter * 8251876Speter * http://www.apache.org/licenses/LICENSE-2.0 9251876Speter * 10251876Speter * Unless required by applicable law or agreed to in writing, software 11251876Speter * distributed under the License is distributed on an "AS IS" BASIS, 12251876Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13251876Speter * See the License for the specific language governing permissions and 14251876Speter * limitations under the License. 15251876Speter */ 16251876Speter 17251876Speter#include "apr_strings.h" 18251876Speter#define APR_WANT_MEMFUNC 19251876Speter#include "apr_want.h" 20251876Speter 21251876Speter#define APU_WANT_DB 22251876Speter#include "apu_want.h" 23251876Speter 24251876Speter#if APR_HAVE_STDLIB_H 25251876Speter#include <stdlib.h> /* for abort() */ 26251876Speter#endif 27251876Speter 28251876Speter#include "apu_config.h" 29251876Speter#include "apu.h" 30251876Speter 31251876Speter#if APU_HAVE_DB 32251876Speter#include "apr_dbm_private.h" 33251876Speter 34251876Speter/* 35251876Speter * We pick up all varieties of Berkeley DB through db.h (included through 36251876Speter * apu_select_dbm.h). This code has been compiled/tested against DB1, 37251876Speter * DB_185, DB2, DB3, and DB4. 38251876Speter */ 39251876Speter 40251876Speter#if defined(DB_VERSION_MAJOR) && (DB_VERSION_MAJOR >= 4) 41251876Speter/* We will treat anything greater than 4.1 as DB4. 42251876Speter * We can treat 4.0 as DB3. 43251876Speter */ 44251876Speter#if DB_VERSION_MAJOR > 4 || (defined(DB_VERSION_MINOR) && (DB_VERSION_MINOR >= 1)) 45251876Speter#define DB_VER 4 46251876Speter#elif DB_VERSION_MAJOR == 4 47251876Speter#define DB_VER 3 48251876Speter#endif 49251876Speter#elif defined(DB_VERSION_MAJOR) && (DB_VERSION_MAJOR == 3) 50251876Speter#define DB_VER 3 51251876Speter#elif defined(DB_VERSION_MAJOR) && (DB_VERSION_MAJOR == 2) 52251876Speter#define DB_VER 2 53251876Speter#else 54251876Speter#define DB_VER 1 55251876Speter#endif 56251876Speter 57251876Spetertypedef struct { 58251876Speter DB *bdb; 59251876Speter#if DB_VER != 1 60251876Speter DBC *curs; 61251876Speter#endif 62251876Speter} real_file_t; 63251876Speter 64251876Speter 65251876Speter#if DB_VER == 1 66251876Speter#define TXN_ARG 67251876Speter#else 68251876Speter#define TXN_ARG NULL, 69251876Speter#endif 70251876Speter 71251876Speter#define GET_BDB(f) (((real_file_t *)(f))->bdb) 72251876Speter 73251876Speter#define do_fetch(bdb, k, v) ((*(bdb)->get)(bdb, TXN_ARG &(k), &(v), 0)) 74251876Speter 75251876Speter#if DB_VER == 1 76251876Speter#include <sys/fcntl.h> 77251876Speter#define APR_DBM_DBMODE_RO O_RDONLY 78251876Speter#define APR_DBM_DBMODE_RW O_RDWR 79251876Speter#define APR_DBM_DBMODE_RWCREATE (O_CREAT | O_RDWR) 80251876Speter#define APR_DBM_DBMODE_RWTRUNC (O_CREAT | O_RDWR | O_TRUNC) 81251876Speter#else 82251876Speter#define APR_DBM_DBMODE_RO DB_RDONLY 83251876Speter#define APR_DBM_DBMODE_RW 0 84251876Speter#define APR_DBM_DBMODE_RWCREATE DB_CREATE 85251876Speter#define APR_DBM_DBMODE_RWTRUNC DB_TRUNCATE 86251876Speter#endif /* DBVER == 1 */ 87251876Speter 88251876Speter/* -------------------------------------------------------------------------- 89251876Speter** 90251876Speter** UTILITY FUNCTIONS 91251876Speter*/ 92251876Speter 93251876Speter/* map a DB error to an apr_status_t */ 94251876Speterstatic apr_status_t db2s(int dberr) 95251876Speter{ 96251876Speter if (dberr != 0) { 97251876Speter /* ### need to fix this */ 98251876Speter return APR_OS_START_USEERR + dberr; 99251876Speter } 100251876Speter 101251876Speter return APR_SUCCESS; 102251876Speter} 103251876Speter 104251876Speter 105251876Speterstatic apr_status_t set_error(apr_dbm_t *dbm, apr_status_t dbm_said) 106251876Speter{ 107251876Speter apr_status_t rv = APR_SUCCESS; 108251876Speter 109251876Speter /* ### ignore whatever the DBM said (dbm_said); ask it explicitly */ 110251876Speter 111251876Speter if (dbm_said == APR_SUCCESS) { 112251876Speter dbm->errcode = 0; 113251876Speter dbm->errmsg = NULL; 114251876Speter } 115251876Speter else { 116251876Speter /* ### need to fix. dberr was tossed in db2s(). */ 117251876Speter /* ### use db_strerror() */ 118251876Speter dbm->errcode = dbm_said; 119251876Speter#if DB_VER == 1 || DB_VER == 2 120251876Speter dbm->errmsg = NULL; 121251876Speter#else 122251876Speter dbm->errmsg = db_strerror(dbm_said - APR_OS_START_USEERR); 123251876Speter#endif 124251876Speter rv = dbm_said; 125251876Speter } 126251876Speter 127251876Speter return rv; 128251876Speter} 129251876Speter 130251876Speter/* -------------------------------------------------------------------------- 131251876Speter** 132251876Speter** DEFINE THE VTABLE FUNCTIONS FOR BERKELEY DB 133251876Speter** 134251876Speter** ### we may need three sets of these: db1, db2, db3 135251876Speter*/ 136251876Speter 137251876Speterstatic apr_status_t vt_db_open(apr_dbm_t **pdb, const char *pathname, 138251876Speter apr_int32_t mode, apr_fileperms_t perm, 139251876Speter apr_pool_t *pool) 140251876Speter{ 141251876Speter real_file_t file; 142251876Speter int dbmode; 143251876Speter 144251876Speter *pdb = NULL; 145251876Speter 146251876Speter switch (mode) { 147251876Speter case APR_DBM_READONLY: 148251876Speter dbmode = APR_DBM_DBMODE_RO; 149251876Speter break; 150251876Speter case APR_DBM_READWRITE: 151251876Speter dbmode = APR_DBM_DBMODE_RW; 152251876Speter break; 153251876Speter case APR_DBM_RWCREATE: 154251876Speter dbmode = APR_DBM_DBMODE_RWCREATE; 155251876Speter break; 156251876Speter case APR_DBM_RWTRUNC: 157251876Speter dbmode = APR_DBM_DBMODE_RWTRUNC; 158251876Speter break; 159251876Speter default: 160251876Speter return APR_EINVAL; 161251876Speter } 162251876Speter 163251876Speter { 164251876Speter int dberr; 165251876Speter 166251876Speter#if DB_VER >= 3 167251876Speter if ((dberr = db_create(&file.bdb, NULL, 0)) == 0) { 168251876Speter if ((dberr = (*file.bdb->open)(file.bdb, 169251876Speter#if DB_VER == 4 170251876Speter NULL, 171251876Speter#endif 172251876Speter pathname, NULL, 173251876Speter DB_HASH, dbmode, 174251876Speter apr_posix_perms2mode(perm))) != 0) { 175251876Speter /* close the DB handler */ 176251876Speter (void) (*file.bdb->close)(file.bdb, 0); 177251876Speter } 178251876Speter } 179251876Speter file.curs = NULL; 180251876Speter#elif DB_VER == 2 181251876Speter dberr = db_open(pathname, DB_HASH, dbmode, apr_posix_perms2mode(perm), 182251876Speter NULL, NULL, &file.bdb); 183251876Speter file.curs = NULL; 184251876Speter#else 185251876Speter file.bdb = dbopen(pathname, dbmode, apr_posix_perms2mode(perm), 186251876Speter DB_HASH, NULL); 187251876Speter if (file.bdb == NULL) 188251876Speter return APR_EGENERAL; /* ### need a better error */ 189251876Speter dberr = 0; 190251876Speter#endif 191251876Speter if (dberr != 0) 192251876Speter return db2s(dberr); 193251876Speter } 194251876Speter 195251876Speter /* we have an open database... return it */ 196251876Speter *pdb = apr_pcalloc(pool, sizeof(**pdb)); 197251876Speter (*pdb)->pool = pool; 198251876Speter (*pdb)->type = &apr_dbm_type_db; 199251876Speter (*pdb)->file = apr_pmemdup(pool, &file, sizeof(file)); 200251876Speter 201251876Speter /* ### register a cleanup to close the DBM? */ 202251876Speter 203251876Speter return APR_SUCCESS; 204251876Speter} 205251876Speter 206251876Speterstatic void vt_db_close(apr_dbm_t *dbm) 207251876Speter{ 208251876Speter (*GET_BDB(dbm->file)->close)(GET_BDB(dbm->file) 209251876Speter#if DB_VER != 1 210251876Speter , 0 211251876Speter#endif 212251876Speter ); 213251876Speter} 214251876Speter 215251876Speterstatic apr_status_t vt_db_fetch(apr_dbm_t *dbm, apr_datum_t key, 216251876Speter apr_datum_t * pvalue) 217251876Speter{ 218251876Speter DBT ckey = { 0 }; 219251876Speter DBT rd = { 0 }; 220251876Speter int dberr; 221251876Speter 222251876Speter ckey.data = key.dptr; 223251876Speter ckey.size = key.dsize; 224251876Speter 225251876Speter dberr = do_fetch(GET_BDB(dbm->file), ckey, rd); 226251876Speter 227251876Speter /* "not found" is not an error. return zero'd value. */ 228251876Speter if (dberr == 229251876Speter#if DB_VER == 1 230251876Speter RET_SPECIAL 231251876Speter#else 232251876Speter DB_NOTFOUND 233251876Speter#endif 234251876Speter ) { 235251876Speter memset(&rd, 0, sizeof(rd)); 236251876Speter dberr = 0; 237251876Speter } 238251876Speter 239251876Speter pvalue->dptr = rd.data; 240251876Speter pvalue->dsize = rd.size; 241251876Speter 242251876Speter /* store the error info into DBM, and return a status code. Also, note 243251876Speter that *pvalue should have been cleared on error. */ 244251876Speter return set_error(dbm, db2s(dberr)); 245251876Speter} 246251876Speter 247251876Speterstatic apr_status_t vt_db_store(apr_dbm_t *dbm, apr_datum_t key, 248251876Speter apr_datum_t value) 249251876Speter{ 250251876Speter apr_status_t rv; 251251876Speter DBT ckey = { 0 }; 252251876Speter DBT cvalue = { 0 }; 253251876Speter 254251876Speter ckey.data = key.dptr; 255251876Speter ckey.size = key.dsize; 256251876Speter 257251876Speter cvalue.data = value.dptr; 258251876Speter cvalue.size = value.dsize; 259251876Speter 260251876Speter rv = db2s((*GET_BDB(dbm->file)->put)(GET_BDB(dbm->file), 261251876Speter TXN_ARG 262251876Speter &ckey, 263251876Speter &cvalue, 264251876Speter 0)); 265251876Speter 266251876Speter /* store any error info into DBM, and return a status code. */ 267251876Speter return set_error(dbm, rv); 268251876Speter} 269251876Speter 270251876Speterstatic apr_status_t vt_db_del(apr_dbm_t *dbm, apr_datum_t key) 271251876Speter{ 272251876Speter apr_status_t rv; 273251876Speter DBT ckey = { 0 }; 274251876Speter 275251876Speter ckey.data = key.dptr; 276251876Speter ckey.size = key.dsize; 277251876Speter 278251876Speter rv = db2s((*GET_BDB(dbm->file)->del)(GET_BDB(dbm->file), 279251876Speter TXN_ARG 280251876Speter &ckey, 281251876Speter 0)); 282251876Speter 283251876Speter /* store any error info into DBM, and return a status code. */ 284251876Speter return set_error(dbm, rv); 285251876Speter} 286251876Speter 287251876Speterstatic int vt_db_exists(apr_dbm_t *dbm, apr_datum_t key) 288251876Speter{ 289251876Speter DBT ckey = { 0 }; /* converted key */ 290251876Speter DBT data = { 0 }; 291251876Speter int dberr; 292251876Speter 293251876Speter ckey.data = key.dptr; 294251876Speter ckey.size = key.dsize; 295251876Speter 296251876Speter dberr = do_fetch(GET_BDB(dbm->file), ckey, data); 297251876Speter 298251876Speter /* note: the result data is "loaned" to us; we don't need to free it */ 299251876Speter 300251876Speter /* DB returns DB_NOTFOUND if it doesn't exist. but we want to say 301251876Speter that *any* error means it doesn't exist. */ 302251876Speter return dberr == 0; 303251876Speter} 304251876Speter 305251876Speterstatic apr_status_t vt_db_firstkey(apr_dbm_t *dbm, apr_datum_t * pkey) 306251876Speter{ 307251876Speter real_file_t *f = dbm->file; 308251876Speter DBT first = { 0 }; 309251876Speter DBT data = { 0 }; 310251876Speter int dberr; 311251876Speter 312251876Speter#if DB_VER == 1 313251876Speter dberr = (*f->bdb->seq)(f->bdb, &first, &data, R_FIRST); 314251876Speter#else 315251876Speter if ((dberr = (*f->bdb->cursor)(f->bdb, NULL, &f->curs 316251876Speter#if DB_VER >= 3 || ((DB_VERSION_MAJOR == 2) && (DB_VERSION_MINOR > 5)) 317251876Speter , 0 318251876Speter#endif 319251876Speter )) == 0) { 320251876Speter dberr = (*f->curs->c_get)(f->curs, &first, &data, DB_FIRST); 321251876Speter if (dberr == DB_NOTFOUND) { 322251876Speter memset(&first, 0, sizeof(first)); 323251876Speter (*f->curs->c_close)(f->curs); 324251876Speter f->curs = NULL; 325251876Speter dberr = 0; 326251876Speter } 327251876Speter } 328251876Speter#endif 329251876Speter 330251876Speter pkey->dptr = first.data; 331251876Speter pkey->dsize = first.size; 332251876Speter 333251876Speter /* store any error info into DBM, and return a status code. */ 334251876Speter return set_error(dbm, db2s(dberr)); 335251876Speter} 336251876Speter 337251876Speterstatic apr_status_t vt_db_nextkey(apr_dbm_t *dbm, apr_datum_t * pkey) 338251876Speter{ 339251876Speter real_file_t *f = dbm->file; 340251876Speter DBT ckey = { 0 }; 341251876Speter DBT data = { 0 }; 342251876Speter int dberr; 343251876Speter 344251876Speter ckey.data = pkey->dptr; 345251876Speter ckey.size = pkey->dsize; 346251876Speter 347251876Speter#if DB_VER == 1 348251876Speter dberr = (*f->bdb->seq)(f->bdb, &ckey, &data, R_NEXT); 349251876Speter if (dberr == RET_SPECIAL) { 350251876Speter dberr = 0; 351251876Speter ckey.data = NULL; 352251876Speter ckey.size = 0; 353251876Speter } 354251876Speter#else 355251876Speter if (f->curs == NULL) 356251876Speter return APR_EINVAL; 357251876Speter 358251876Speter dberr = (*f->curs->c_get)(f->curs, &ckey, &data, DB_NEXT); 359251876Speter if (dberr == DB_NOTFOUND) { 360251876Speter (*f->curs->c_close)(f->curs); 361251876Speter f->curs = NULL; 362251876Speter dberr = 0; 363251876Speter ckey.data = NULL; 364251876Speter ckey.size = 0; 365251876Speter } 366251876Speter#endif 367251876Speter 368251876Speter pkey->dptr = ckey.data; 369251876Speter pkey->dsize = ckey.size; 370251876Speter 371251876Speter /* store any error info into DBM, and return a status code. */ 372251876Speter /* ### or use db2s(dberr) instead of APR_SUCCESS? */ 373251876Speter return set_error(dbm, APR_SUCCESS); 374251876Speter} 375251876Speter 376251876Speterstatic void vt_db_freedatum(apr_dbm_t *dbm, apr_datum_t data) 377251876Speter{ 378251876Speter /* nothing to do */ 379251876Speter} 380251876Speter 381251876Speterstatic void vt_db_usednames(apr_pool_t *pool, const char *pathname, 382251876Speter const char **used1, const char **used2) 383251876Speter{ 384251876Speter *used1 = apr_pstrdup(pool, pathname); 385251876Speter *used2 = NULL; 386251876Speter} 387251876Speter 388251876Speter 389251876SpeterAPU_MODULE_DECLARE_DATA const apr_dbm_type_t apr_dbm_type_db = { 390251876Speter "db", 391251876Speter 392251876Speter vt_db_open, 393251876Speter vt_db_close, 394251876Speter vt_db_fetch, 395251876Speter vt_db_store, 396251876Speter vt_db_del, 397251876Speter vt_db_exists, 398251876Speter vt_db_firstkey, 399251876Speter vt_db_nextkey, 400251876Speter vt_db_freedatum, 401251876Speter vt_db_usednames 402251876Speter}; 403251876Speter 404251876Speter#endif /* APU_HAVE_DB */ 405