1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "apr.h"
18#include "apr_dso.h"
19#include "apr_hash.h"
20#include "apr_errno.h"
21#include "apr_pools.h"
22#include "apr_strings.h"
23#define APR_WANT_MEMFUNC
24#define APR_WANT_STRFUNC
25#include "apr_want.h"
26#include "apr_general.h"
27#include "apr_atomic.h"
28
29#include "apu_config.h"
30#include "apu.h"
31#include "apu_internal.h"
32#include "apu_version.h"
33#include "apr_dbm_private.h"
34#include "apu_select_dbm.h"
35#include "apr_dbm.h"
36#include "apr_dbm_private.h"
37
38/* ### note: the setting of DBM_VTABLE will go away once we have multiple
39   ### DBMs in here.
40   ### Well, that day is here.  So, do we remove DBM_VTABLE and the old
41   ### API entirely?  Oh, what to do.  We need an APU_DEFAULT_DBM #define.
42   ### Sounds like a job for autoconf. */
43
44#if APU_USE_DB
45#define DBM_VTABLE apr_dbm_type_db
46#define DBM_NAME   "db"
47#elif APU_USE_GDBM
48#define DBM_VTABLE apr_dbm_type_gdbm
49#define DBM_NAME   "gdbm"
50#elif APU_USE_NDBM
51#define DBM_VTABLE apr_dbm_type_ndbm
52#define DBM_NAME   "ndbm"
53#elif APU_USE_SDBM
54#define DBM_VTABLE apr_dbm_type_sdbm
55#define DBM_NAME   "sdbm"
56#else /* Not in the USE_xDBM list above */
57#error a DBM implementation was not specified
58#endif
59
60#if APU_DSO_BUILD
61
62static apr_hash_t *drivers = NULL;
63static apr_uint32_t initialised = 0, in_init = 1;
64
65static apr_status_t dbm_term(void *ptr)
66{
67    /* set drivers to NULL so init can work again */
68    drivers = NULL;
69
70    /* Everything else we need is handled by cleanups registered
71     * when we created mutexes and loaded DSOs
72     */
73    return APR_SUCCESS;
74}
75
76#endif /* APU_DSO_BUILD */
77
78static apr_status_t dbm_open_type(apr_dbm_type_t const* * vtable,
79                                  const char *type,
80                                  apr_pool_t *pool)
81{
82#if !APU_DSO_BUILD
83
84    *vtable = NULL;
85    if (!strcasecmp(type, "default"))     *vtable = &DBM_VTABLE;
86#if APU_HAVE_DB
87    else if (!strcasecmp(type, "db"))     *vtable = &apr_dbm_type_db;
88#endif
89    else if (*type && !strcasecmp(type + 1, "dbm")) {
90#if APU_HAVE_GDBM
91        if (*type == 'G' || *type == 'g') *vtable = &apr_dbm_type_gdbm;
92#endif
93#if APU_HAVE_NDBM
94        if (*type == 'N' || *type == 'n') *vtable = &apr_dbm_type_ndbm;
95#endif
96#if APU_HAVE_SDBM
97        if (*type == 'S' || *type == 's') *vtable = &apr_dbm_type_sdbm;
98#endif
99        /* avoid empty block */ ;
100    }
101    if (*vtable)
102        return APR_SUCCESS;
103    return APR_ENOTIMPL;
104
105#else /* APU_DSO_BUILD */
106
107    char modname[32];
108    char symname[34];
109    apr_dso_handle_sym_t symbol;
110    apr_status_t rv;
111    int usertype = 0;
112
113    if (!strcasecmp(type, "default"))        type = DBM_NAME;
114    else if (!strcasecmp(type, "db"))        type = "db";
115    else if (*type && !strcasecmp(type + 1, "dbm")) {
116        if      (*type == 'G' || *type == 'g') type = "gdbm";
117        else if (*type == 'N' || *type == 'n') type = "ndbm";
118        else if (*type == 'S' || *type == 's') type = "sdbm";
119    }
120    else usertype = 1;
121
122    if (apr_atomic_inc32(&initialised)) {
123        apr_atomic_set32(&initialised, 1); /* prevent wrap-around */
124
125        while (apr_atomic_read32(&in_init)) /* wait until we get fully inited */
126            ;
127    }
128    else {
129        apr_pool_t *parent;
130
131        /* Top level pool scope, need process-scope lifetime */
132        for (parent = apr_pool_parent_get(pool);
133             parent && parent != pool;
134             parent = apr_pool_parent_get(pool))
135            pool = parent;
136
137        /* deprecate in 2.0 - permit implicit initialization */
138        apu_dso_init(pool);
139
140        drivers = apr_hash_make(pool);
141        apr_hash_set(drivers, "sdbm", APR_HASH_KEY_STRING, &apr_dbm_type_sdbm);
142
143        apr_pool_cleanup_register(pool, NULL, dbm_term,
144                                  apr_pool_cleanup_null);
145
146        apr_atomic_dec32(&in_init);
147    }
148
149    rv = apu_dso_mutex_lock();
150    if (rv) {
151        *vtable = NULL;
152        return rv;
153    }
154
155    *vtable = apr_hash_get(drivers, type, APR_HASH_KEY_STRING);
156    if (*vtable) {
157        apu_dso_mutex_unlock();
158        return APR_SUCCESS;
159    }
160
161    /* The driver DSO must have exactly the same lifetime as the
162     * drivers hash table; ignore the passed-in pool */
163    pool = apr_hash_pool_get(drivers);
164
165#if defined(NETWARE)
166    apr_snprintf(modname, sizeof(modname), "dbm%s.nlm", type);
167#elif defined(WIN32) || defined (__CYGWIN__)
168    apr_snprintf(modname, sizeof(modname),
169                 "apr_dbm_%s-" APU_STRINGIFY(APU_MAJOR_VERSION) ".dll", type);
170#else
171    apr_snprintf(modname, sizeof(modname),
172                 "apr_dbm_%s-" APU_STRINGIFY(APU_MAJOR_VERSION) ".so", type);
173#endif
174    apr_snprintf(symname, sizeof(symname), "apr_dbm_type_%s", type);
175
176    rv = apu_dso_load(NULL, &symbol, modname, symname, pool);
177    if (rv == APR_SUCCESS || rv == APR_EINIT) { /* previously loaded?!? */
178        *vtable = symbol;
179        if (usertype)
180            type = apr_pstrdup(pool, type);
181        apr_hash_set(drivers, type, APR_HASH_KEY_STRING, *vtable);
182        rv = APR_SUCCESS;
183    }
184    else
185        *vtable = NULL;
186
187    apu_dso_mutex_unlock();
188    return rv;
189
190#endif /* APU_DSO_BUILD */
191}
192
193APU_DECLARE(apr_status_t) apr_dbm_open_ex(apr_dbm_t **pdb, const char *type,
194                                          const char *pathname,
195                                          apr_int32_t mode,
196                                          apr_fileperms_t perm,
197                                          apr_pool_t *pool)
198{
199    apr_dbm_type_t const* vtable = NULL;
200    apr_status_t rv = dbm_open_type(&vtable, type, pool);
201
202    if (rv == APR_SUCCESS) {
203        rv = (vtable->open)(pdb, pathname, mode, perm, pool);
204    }
205    return rv;
206}
207
208APU_DECLARE(apr_status_t) apr_dbm_open(apr_dbm_t **pdb, const char *pathname,
209                                       apr_int32_t mode, apr_fileperms_t perm,
210                                       apr_pool_t *pool)
211{
212    return apr_dbm_open_ex(pdb, DBM_NAME, pathname, mode, perm, pool);
213}
214
215APU_DECLARE(void) apr_dbm_close(apr_dbm_t *dbm)
216{
217    (*dbm->type->close)(dbm);
218}
219
220APU_DECLARE(apr_status_t) apr_dbm_fetch(apr_dbm_t *dbm, apr_datum_t key,
221                                        apr_datum_t *pvalue)
222{
223    return (*dbm->type->fetch)(dbm, key, pvalue);
224}
225
226APU_DECLARE(apr_status_t) apr_dbm_store(apr_dbm_t *dbm, apr_datum_t key,
227                                        apr_datum_t value)
228{
229    return (*dbm->type->store)(dbm, key, value);
230}
231
232APU_DECLARE(apr_status_t) apr_dbm_delete(apr_dbm_t *dbm, apr_datum_t key)
233{
234    return (*dbm->type->del)(dbm, key);
235}
236
237APU_DECLARE(int) apr_dbm_exists(apr_dbm_t *dbm, apr_datum_t key)
238{
239    return (*dbm->type->exists)(dbm, key);
240}
241
242APU_DECLARE(apr_status_t) apr_dbm_firstkey(apr_dbm_t *dbm, apr_datum_t *pkey)
243{
244    return (*dbm->type->firstkey)(dbm, pkey);
245}
246
247APU_DECLARE(apr_status_t) apr_dbm_nextkey(apr_dbm_t *dbm, apr_datum_t *pkey)
248{
249    return (*dbm->type->nextkey)(dbm, pkey);
250}
251
252APU_DECLARE(void) apr_dbm_freedatum(apr_dbm_t *dbm, apr_datum_t data)
253{
254    (*dbm->type->freedatum)(dbm, data);
255}
256
257APU_DECLARE(char *) apr_dbm_geterror(apr_dbm_t *dbm, int *errcode,
258                                     char *errbuf, apr_size_t errbufsize)
259{
260    if (errcode != NULL)
261        *errcode = dbm->errcode;
262
263    /* assert: errbufsize > 0 */
264
265    if (dbm->errmsg == NULL)
266        *errbuf = '\0';
267    else
268        (void) apr_cpystrn(errbuf, dbm->errmsg, errbufsize);
269    return errbuf;
270}
271
272APU_DECLARE(apr_status_t) apr_dbm_get_usednames_ex(apr_pool_t *p,
273                                                   const char *type,
274                                                   const char *pathname,
275                                                   const char **used1,
276                                                   const char **used2)
277{
278    apr_dbm_type_t const* vtable;
279    apr_status_t rv = dbm_open_type(&vtable, type, p);
280
281    if (rv == APR_SUCCESS) {
282        (vtable->getusednames)(p, pathname, used1, used2);
283        return APR_SUCCESS;
284    }
285    return rv;
286}
287
288APU_DECLARE(void) apr_dbm_get_usednames(apr_pool_t *p,
289                                        const char *pathname,
290                                        const char **used1,
291                                        const char **used2)
292{
293    apr_dbm_get_usednames_ex(p, DBM_NAME, pathname, used1, used2);
294}
295
296/* Most DBM libraries take a POSIX mode for creating files.  Don't trust
297 * the mode_t type, some platforms may not support it, int is safe.
298 */
299APU_DECLARE(int) apr_posix_perms2mode(apr_fileperms_t perm)
300{
301    int mode = 0;
302
303    mode |= 0700 & (perm >> 2); /* User  is off-by-2 bits */
304    mode |= 0070 & (perm >> 1); /* Group is off-by-1 bit */
305    mode |= 0007 & (perm);      /* World maps 1 for 1 */
306    return mode;
307}
308