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 = pool;  parent; parent = apr_pool_parent_get(pool))
133             pool = parent;
134
135        /* deprecate in 2.0 - permit implicit initialization */
136        apu_dso_init(pool);
137
138        drivers = apr_hash_make(pool);
139        apr_hash_set(drivers, "sdbm", APR_HASH_KEY_STRING, &apr_dbm_type_sdbm);
140
141        apr_pool_cleanup_register(pool, NULL, dbm_term,
142                                  apr_pool_cleanup_null);
143
144        apr_atomic_dec32(&in_init);
145    }
146
147    rv = apu_dso_mutex_lock();
148    if (rv) {
149        *vtable = NULL;
150        return rv;
151    }
152
153    *vtable = apr_hash_get(drivers, type, APR_HASH_KEY_STRING);
154    if (*vtable) {
155        apu_dso_mutex_unlock();
156        return APR_SUCCESS;
157    }
158
159    /* The driver DSO must have exactly the same lifetime as the
160     * drivers hash table; ignore the passed-in pool */
161    pool = apr_hash_pool_get(drivers);
162
163#if defined(NETWARE)
164    apr_snprintf(modname, sizeof(modname), "dbm%s.nlm", type);
165#elif defined(WIN32)
166    apr_snprintf(modname, sizeof(modname),
167                 "apr_dbm_%s-" APU_STRINGIFY(APU_MAJOR_VERSION) ".dll", type);
168#else
169    apr_snprintf(modname, sizeof(modname),
170                 "apr_dbm_%s-" APU_STRINGIFY(APU_MAJOR_VERSION) ".so", type);
171#endif
172    apr_snprintf(symname, sizeof(symname), "apr_dbm_type_%s", type);
173
174    rv = apu_dso_load(NULL, &symbol, modname, symname, pool);
175    if (rv == APR_SUCCESS || rv == APR_EINIT) { /* previously loaded?!? */
176        *vtable = symbol;
177        if (usertype)
178            type = apr_pstrdup(pool, type);
179        apr_hash_set(drivers, type, APR_HASH_KEY_STRING, *vtable);
180        rv = APR_SUCCESS;
181    }
182    else
183        *vtable = NULL;
184
185    apu_dso_mutex_unlock();
186    return rv;
187
188#endif /* APU_DSO_BUILD */
189}
190
191APU_DECLARE(apr_status_t) apr_dbm_open_ex(apr_dbm_t **pdb, const char *type,
192                                          const char *pathname,
193                                          apr_int32_t mode,
194                                          apr_fileperms_t perm,
195                                          apr_pool_t *pool)
196{
197    apr_dbm_type_t const* vtable = NULL;
198    apr_status_t rv = dbm_open_type(&vtable, type, pool);
199
200    if (rv == APR_SUCCESS) {
201        rv = (vtable->open)(pdb, pathname, mode, perm, pool);
202    }
203    return rv;
204}
205
206APU_DECLARE(apr_status_t) apr_dbm_open(apr_dbm_t **pdb, const char *pathname,
207                                       apr_int32_t mode, apr_fileperms_t perm,
208                                       apr_pool_t *pool)
209{
210    return apr_dbm_open_ex(pdb, DBM_NAME, pathname, mode, perm, pool);
211}
212
213APU_DECLARE(void) apr_dbm_close(apr_dbm_t *dbm)
214{
215    (*dbm->type->close)(dbm);
216}
217
218APU_DECLARE(apr_status_t) apr_dbm_fetch(apr_dbm_t *dbm, apr_datum_t key,
219                                        apr_datum_t *pvalue)
220{
221    return (*dbm->type->fetch)(dbm, key, pvalue);
222}
223
224APU_DECLARE(apr_status_t) apr_dbm_store(apr_dbm_t *dbm, apr_datum_t key,
225                                        apr_datum_t value)
226{
227    return (*dbm->type->store)(dbm, key, value);
228}
229
230APU_DECLARE(apr_status_t) apr_dbm_delete(apr_dbm_t *dbm, apr_datum_t key)
231{
232    return (*dbm->type->del)(dbm, key);
233}
234
235APU_DECLARE(int) apr_dbm_exists(apr_dbm_t *dbm, apr_datum_t key)
236{
237    return (*dbm->type->exists)(dbm, key);
238}
239
240APU_DECLARE(apr_status_t) apr_dbm_firstkey(apr_dbm_t *dbm, apr_datum_t *pkey)
241{
242    return (*dbm->type->firstkey)(dbm, pkey);
243}
244
245APU_DECLARE(apr_status_t) apr_dbm_nextkey(apr_dbm_t *dbm, apr_datum_t *pkey)
246{
247    return (*dbm->type->nextkey)(dbm, pkey);
248}
249
250APU_DECLARE(void) apr_dbm_freedatum(apr_dbm_t *dbm, apr_datum_t data)
251{
252    (*dbm->type->freedatum)(dbm, data);
253}
254
255APU_DECLARE(char *) apr_dbm_geterror(apr_dbm_t *dbm, int *errcode,
256                                     char *errbuf, apr_size_t errbufsize)
257{
258    if (errcode != NULL)
259        *errcode = dbm->errcode;
260
261    /* assert: errbufsize > 0 */
262
263    if (dbm->errmsg == NULL)
264        *errbuf = '\0';
265    else
266        (void) apr_cpystrn(errbuf, dbm->errmsg, errbufsize);
267    return errbuf;
268}
269
270APU_DECLARE(apr_status_t) apr_dbm_get_usednames_ex(apr_pool_t *p,
271                                                   const char *type,
272                                                   const char *pathname,
273                                                   const char **used1,
274                                                   const char **used2)
275{
276    apr_dbm_type_t const* vtable;
277    apr_status_t rv = dbm_open_type(&vtable, type, p);
278
279    if (rv == APR_SUCCESS) {
280        (vtable->getusednames)(p, pathname, used1, used2);
281        return APR_SUCCESS;
282    }
283    return rv;
284}
285
286APU_DECLARE(void) apr_dbm_get_usednames(apr_pool_t *p,
287                                        const char *pathname,
288                                        const char **used1,
289                                        const char **used2)
290{
291    apr_dbm_get_usednames_ex(p, DBM_NAME, pathname, used1, used2);
292}
293
294/* Most DBM libraries take a POSIX mode for creating files.  Don't trust
295 * the mode_t type, some platforms may not support it, int is safe.
296 */
297APU_DECLARE(int) apr_posix_perms2mode(apr_fileperms_t perm)
298{
299    int mode = 0;
300
301    mode |= 0700 & (perm >> 2); /* User  is off-by-2 bits */
302    mode |= 0070 & (perm >> 1); /* Group is off-by-1 bit */
303    mode |= 0007 & (perm);      /* World maps 1 for 1 */
304    return mode;
305}
306