/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "apr.h" #include "apr_dso.h" #include "apr_hash.h" #include "apr_errno.h" #include "apr_pools.h" #include "apr_strings.h" #define APR_WANT_MEMFUNC #define APR_WANT_STRFUNC #include "apr_want.h" #include "apr_general.h" #include "apr_atomic.h" #include "apu.h" #include "apr_private.h" #include "apu_internal.h" #include "apr_version.h" #include "apr_dbm_private.h" #include "apu_select_dbm.h" #include "apr_dbm.h" #include "apr_dbm_private.h" /* ### note: the setting of DBM_VTABLE will go away once we have multiple ### DBMs in here. ### Well, that day is here. So, do we remove DBM_VTABLE and the old ### API entirely? Oh, what to do. We need an APU_DEFAULT_DBM #define. ### Sounds like a job for autoconf. */ #if APU_USE_DB #define DBM_VTABLE apr_dbm_type_db #define DBM_NAME "db" #elif APU_USE_GDBM #define DBM_VTABLE apr_dbm_type_gdbm #define DBM_NAME "gdbm" #elif APU_USE_NDBM #define DBM_VTABLE apr_dbm_type_ndbm #define DBM_NAME "ndbm" #elif APU_USE_SDBM #define DBM_VTABLE apr_dbm_type_sdbm #define DBM_NAME "sdbm" #else /* Not in the USE_xDBM list above */ #error a DBM implementation was not specified #endif #define ERROR_SIZE 1024 #if APR_HAVE_MODULAR_DSO static apr_hash_t *drivers = NULL; static apr_uint32_t initialised = 0, in_init = 1; static apr_status_t dbm_term(void *ptr) { /* set drivers to NULL so init can work again */ drivers = NULL; /* Everything else we need is handled by cleanups registered * when we created mutexes and loaded DSOs */ return APR_SUCCESS; } #endif /* APR_HAVE_MODULAR_DSO */ APR_DECLARE(apr_status_t) apr_dbm_get_driver(const apr_dbm_driver_t **vtable, const char *type, const apu_err_t **result, apr_pool_t *pool) { #if !APR_HAVE_MODULAR_DSO if (result) { *result = NULL; /* until further notice */ } if (!type) { type = DBM_NAME; } *vtable = NULL; if (!strcasecmp(type, "default")) *vtable = &DBM_VTABLE; #if APU_HAVE_DB else if (!strcasecmp(type, "db")) *vtable = &apr_dbm_type_db; #endif else if (*type && !strcasecmp(type + 1, "dbm")) { #if APU_HAVE_GDBM if (*type == 'G' || *type == 'g') *vtable = &apr_dbm_type_gdbm; #endif #if APU_HAVE_NDBM if (*type == 'N' || *type == 'n') *vtable = &apr_dbm_type_ndbm; #endif #if APU_HAVE_SDBM if (*type == 'S' || *type == 's') *vtable = &apr_dbm_type_sdbm; #endif /* avoid empty block */ ; } if (*vtable) { return APR_SUCCESS; } if (result && !*result) { apu_err_t *err = apr_pcalloc(pool, sizeof(apu_err_t)); if (err) { apr_status_t rv = APR_ENOTIMPL; err->msg = apr_psprintf(pool, "%pm", &rv); err->reason = apr_pstrdup(pool, type); *result = err; } } return APR_ENOTIMPL; #else /* APR_HAVE_MODULAR_DSO */ char modname[32]; char symname[34]; apr_dso_handle_t *dso; apr_dso_handle_sym_t symbol; apr_status_t rv; int usertype = 0; if (result) { *result = NULL; /* until further notice */ } if (!type) { type = DBM_NAME; } if (!strcasecmp(type, "default")) type = DBM_NAME; else if (!strcasecmp(type, "db")) type = "db"; else if (*type && !strcasecmp(type + 1, "dbm")) { if (*type == 'G' || *type == 'g') type = "gdbm"; else if (*type == 'N' || *type == 'n') type = "ndbm"; else if (*type == 'S' || *type == 's') type = "sdbm"; } else usertype = 1; if (apr_atomic_inc32(&initialised)) { apr_atomic_set32(&initialised, 1); /* prevent wrap-around */ while (apr_atomic_read32(&in_init)) /* wait until we get fully inited */ ; } else { apr_pool_t *parent; /* Top level pool scope, need process-scope lifetime */ for (parent = apr_pool_parent_get(pool); parent && parent != pool; parent = apr_pool_parent_get(pool)) pool = parent; /* deprecate in 2.0 - permit implicit initialization */ apu_dso_init(pool); drivers = apr_hash_make(pool); apr_hash_set(drivers, "sdbm", APR_HASH_KEY_STRING, &apr_dbm_type_sdbm); apr_pool_cleanup_register(pool, NULL, dbm_term, apr_pool_cleanup_null); apr_atomic_dec32(&in_init); } rv = apu_dso_mutex_lock(); if (rv) { *vtable = NULL; return rv; } *vtable = apr_hash_get(drivers, type, APR_HASH_KEY_STRING); if (*vtable) { apu_dso_mutex_unlock(); return APR_SUCCESS; } /* The driver DSO must have exactly the same lifetime as the * drivers hash table; ignore the passed-in pool */ pool = apr_hash_pool_get(drivers); #if defined(NETWARE) apr_snprintf(modname, sizeof(modname), "dbm%s.nlm", type); #elif defined(WIN32) || defined (__CYGWIN__) apr_snprintf(modname, sizeof(modname), "apr_dbm_%s-" APR_STRINGIFY(APR_MAJOR_VERSION) ".dll", type); #else apr_snprintf(modname, sizeof(modname), "apr_dbm_%s-" APR_STRINGIFY(APR_MAJOR_VERSION) ".so", type); #endif apr_snprintf(symname, sizeof(symname), "apr_dbm_type_%s", type); rv = apu_dso_load(&dso, &symbol, modname, symname, pool); if (rv == APR_SUCCESS || rv == APR_EINIT) { /* previously loaded?!? */ *vtable = symbol; if (usertype) type = apr_pstrdup(pool, type); apr_hash_set(drivers, type, APR_HASH_KEY_STRING, *vtable); rv = APR_SUCCESS; } else *vtable = NULL; apu_dso_mutex_unlock(); if (APR_SUCCESS != rv && result && !*result) { char *buffer = apr_pcalloc(pool, ERROR_SIZE); apu_err_t *err = apr_pcalloc(pool, sizeof(apu_err_t)); if (err && buffer) { apr_dso_error(dso, buffer, ERROR_SIZE - 1); err->msg = buffer; err->reason = apr_pstrdup(pool, modname); *result = err; } } return rv; #endif /* APR_HAVE_MODULAR_DSO */ } APR_DECLARE(apr_status_t) apr_dbm_open_ex(apr_dbm_t **pdb, const char *type, const char *pathname, apr_int32_t mode, apr_fileperms_t perm, apr_pool_t *pool) { apr_dbm_driver_t const* vtable = NULL; apr_status_t rv = apr_dbm_get_driver(&vtable, type, NULL, pool); if (rv == APR_SUCCESS) { rv = (vtable->open)(pdb, pathname, mode, perm, pool); } return rv; } APR_DECLARE(apr_status_t) apr_dbm_open(apr_dbm_t **pdb, const char *pathname, apr_int32_t mode, apr_fileperms_t perm, apr_pool_t *pool) { return apr_dbm_open_ex(pdb, DBM_NAME, pathname, mode, perm, pool); } APR_DECLARE(apr_status_t) apr_dbm_open2(apr_dbm_t **pdb, const apr_dbm_driver_t *vtable, const char *pathname, apr_int32_t mode, apr_fileperms_t perm, apr_pool_t *pool) { return (vtable->open)(pdb, pathname, mode, perm, pool); } APR_DECLARE(void) apr_dbm_close(apr_dbm_t *dbm) { (*dbm->type->close)(dbm); } APR_DECLARE(apr_status_t) apr_dbm_fetch(apr_dbm_t *dbm, apr_datum_t key, apr_datum_t *pvalue) { return (*dbm->type->fetch)(dbm, key, pvalue); } APR_DECLARE(apr_status_t) apr_dbm_store(apr_dbm_t *dbm, apr_datum_t key, apr_datum_t value) { return (*dbm->type->store)(dbm, key, value); } APR_DECLARE(apr_status_t) apr_dbm_delete(apr_dbm_t *dbm, apr_datum_t key) { return (*dbm->type->del)(dbm, key); } APR_DECLARE(int) apr_dbm_exists(apr_dbm_t *dbm, apr_datum_t key) { return (*dbm->type->exists)(dbm, key); } APR_DECLARE(apr_status_t) apr_dbm_firstkey(apr_dbm_t *dbm, apr_datum_t *pkey) { return (*dbm->type->firstkey)(dbm, pkey); } APR_DECLARE(apr_status_t) apr_dbm_nextkey(apr_dbm_t *dbm, apr_datum_t *pkey) { return (*dbm->type->nextkey)(dbm, pkey); } APR_DECLARE(void) apr_dbm_freedatum(apr_dbm_t *dbm, apr_datum_t data) { (*dbm->type->freedatum)(dbm, data); } APR_DECLARE(char *) apr_dbm_geterror(apr_dbm_t *dbm, int *errcode, char *errbuf, apr_size_t errbufsize) { if (errcode != NULL) *errcode = dbm->errcode; /* assert: errbufsize > 0 */ if (dbm->errmsg == NULL) *errbuf = '\0'; else (void) apr_cpystrn(errbuf, dbm->errmsg, errbufsize); return errbuf; } APR_DECLARE(apr_status_t) apr_dbm_get_usednames_ex(apr_pool_t *p, const char *type, const char *pathname, const char **used1, const char **used2) { apr_dbm_driver_t const* vtable; apr_status_t rv = apr_dbm_get_driver(&vtable, type, NULL, p); if (rv == APR_SUCCESS) { (vtable->getusednames)(p, pathname, used1, used2); return APR_SUCCESS; } return rv; } APR_DECLARE(void) apr_dbm_get_usednames(apr_pool_t *p, const char *pathname, const char **used1, const char **used2) { apr_dbm_get_usednames_ex(p, DBM_NAME, pathname, used1, used2); } /* Most DBM libraries take a POSIX mode for creating files. Don't trust * the mode_t type, some platforms may not support it, int is safe. */ APR_DECLARE(int) apr_posix_perms2mode(apr_fileperms_t perm) { int mode = 0; mode |= 0700 & (perm >> 2); /* User is off-by-2 bits */ mode |= 0070 & (perm >> 1); /* Group is off-by-1 bit */ mode |= 0007 & (perm); /* World maps 1 for 1 */ return mode; }