/*
* Copyright (c) 2003-2005 Sendmail, Inc. and its suppliers.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*/
#include "sm/generic.h"
SM_RCSID("@(#)$Id: bdb.c,v 1.43 2006/10/05 04:27:38 ca Exp $")
#include "sm/assert.h"
#include "sm/error.h"
#include "sm/memops.h"
#include "sm/heap.h"
#include "map.h"
#include "sm/map.h"
#include "sm/maps.h"
#include "sm/mapc.h"
#include "sm/mapclasses.h"
#include "sm/bdb.h"
/*
ToDo: implementation of map for Berkeley DB.
translate BDB error numbers into sm?
or can we simply reserve the BDB error range?
locking?
*/
#ifndef DBMMODE
# define DBMMODE 0640
#endif
/* static sm_map_open_F sm_bdb_open; */
/* static sm_map_close_F sm_bdb_close; */
static sm_map_alloc_F sm_bdb_alloc;
static sm_map_free_F sm_bdb_free;
static sm_map_locate_F sm_bdb_locate;
static sm_map_first_F sm_bdb_first;
static sm_map_next_F sm_bdb_next;
/*
** These are types of databases.
*/
#define SMDB_TYPE_DEFAULT NULL
#define SMDB_TYPE_HASH "hash"
#define SMDB_TYPE_BTREE "btree"
#define SMDB_TYPE_NDBM "dbm"
/*
** BDB_TYPE2BDB -- Translates database type to BDB type.
**
** Parameters:
** type -- The type to translate.
**
** Returns:
** The BDB type that corresponds to type.
*/
static DBTYPE
bdb_type2bdb2(const sm_cstr_P type)
{
if (type == SMDB_TYPE_DEFAULT)
return DB_HASH;
if (strncmp((const char*)sm_cstr_data(type),
SMDB_TYPE_HASH, sm_cstr_getlen(type)) == 0)
return DB_HASH;
if (strncmp((const char*)sm_cstr_data(type), SMDB_TYPE_BTREE,
sm_cstr_getlen(type)) == 0)
return DB_BTREE;
return DB_UNKNOWN;
}
/*
** SM_BDB_SETOPT -- set options for map
**
** Parameters:
** map -- map
** ap -- options
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
sm_bdb_setopt(sm_map_P map, va_list ap)
{
sm_ret_T ret;
int k, v;
DB *db;
SM_IS_MAP(map);
db = (DB *) map->sm_map_db;
if (db == NULL)
return sm_error_perm(SM_EM_MAP, ENOENT);
ret = SM_SUCCESS;
for (;;)
{
k = va_arg(ap, int);
if (k == SMPO_END)
break;
switch (k)
{
case SMPO_BDB_DB_CACHE_SIZE:
v = va_arg(ap, int);
ret = db->set_cachesize(db, 0, v, 1);
if (ret != 0)
{
ret = BDB_ERR2RET(ret);
goto error;
}
break;
case SMPO_BDB_DB_HASH_NELEM:
v = va_arg(ap, int);
ret = db->set_h_nelem(db, v);
if (ret != 0)
{
ret = BDB_ERR2RET(ret);
goto error;
}
default:
/* silently ignore bogus options? */
break;
}
}
error:
return ret;
}
/*
** SM_BDB_GETOPT -- get options for map
**
** Parameters:
** map -- map
** which -- which option?
** valp -- pointer to place where result should be stored
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
sm_bdb_getopt(sm_map_P map, int which, void *valp)
{
sm_ret_T ret;
DB *db;
SM_IS_MAP(map);
db = (DB *) map->sm_map_db;
if (db == NULL)
return sm_error_perm(SM_EM_MAP, ENOENT);
/* ... */
ret = SM_SUCCESS;
return ret;
}
/*
** SM_BDB_CLOSE -- close map
** more parameters???
**
** Parameters:
** map -- map
** flags -- currently ignored
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
sm_bdb_close(sm_map_P map, uint32_t flags)
{
sm_ret_T ret;
sm_mapc_P mapc;
DB *db;
SM_IS_MAP(map);
mapc = map->sm_map_class;
SM_IS_MAPC(mapc);
ret = SM_SUCCESS;
db = (DB *) map->sm_map_db;
if (db == NULL)
return sm_error_perm(SM_EM_MAP, ENOENT);
/* close BDB map */
ret = db->close(db, SM_IS_FLAG(map->sm_map_openflags, SMAP_MODE_RDONLY)
? DB_NOSYNC : 0);
map->sm_map_db = NULL;
return ret;
}
/*
** SM_BDB_DESTROY -- destroy map
** XXX more parameters...
**
** Parameters:
** map -- map
** flags -- flags for map
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
sm_bdb_destroy(sm_map_P map, uint32_t flags)
{
sm_ret_T ret;
sm_mapc_P mapc;
DB *db;
SM_IS_MAP(map);
mapc = map->sm_map_class;
SM_IS_MAPC(mapc);
ret = SM_SUCCESS;
db = (DB *) map->sm_map_db;
if (db == NULL)
return sm_error_perm(SM_EM_MAP, ENOENT);
/* call db destroy? */
map->sm_map_db = NULL;
return ret;
}
/*
** SM_BDB_CREATE -- create map
**
** Parameters:
** mapc -- map context
** type -- type of map
** flags -- flags for map
** map -- map
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
sm_bdb_create(sm_mapc_P mapc, const sm_cstr_P type, uint32_t flags, sm_map_P map)
{
sm_ret_T ret;
DB *db;
SM_IS_MAPC(mapc);
SM_REQUIRE(map != NULL);
db = NULL;
/* create BDB map ... */
ret = db_create(&db, NULL, 0);
if (ret == 0)
{
map->sm_map_db = db;
map->sm_map_caps = SMMAP_CAPS_LTALL;
}
/* XXX map error code? */
return ret;
}
/*
** BDB_OPEN_FLAGS -- translate external (map) flags into internal flags
**
** Paramters:
** flags -- map flags
**
** Returns:
** Internal flag value matching user selected flags
*/
static uint32_t
bdb_open_flags(uint32_t flags)
{
uint32_t ret;
#if MTA_USE_PTHREADS
ret = DB_THREAD;
#else
ret = 0;
#endif
if (SM_IS_FLAG(flags, SMAP_MODE_RDONLY))
ret |= DB_RDONLY;
if (SM_IS_FLAG(flags, SMAP_MODE_CREATE))
ret |= DB_CREATE;
return ret;
}
/*
** SM_BDB_OPEN -- open map
**
** Parameters:
** mapc -- map context
** type -- type of map
** flags -- flags for opening map
** path -- path of map
** mode -- open mode (currently ignored, DBMMODE is used)
** map -- map
** ap -- additional argument
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
sm_bdb_open(sm_mapc_P mapc, const sm_cstr_P type, uint32_t flags, const char *path, int mode, sm_map_P map, va_list ap)
{
sm_ret_T ret;
DB *db;
DBTYPE dbtype;
SM_IS_MAPC(mapc);
SM_REQUIRE(map != NULL);
db = NULL;
ret = SM_SUCCESS;
dbtype = bdb_type2bdb2(type);
db = map->sm_map_db;
#ifdef DB_CACHE_SIZE
ret = db->set_cachesize(db, 0, DB_CACHE_SIZE, 0);
if (ret != 0)
{
ret = BDB_ERR2RET(ret);
goto error;
}
#endif /* DB_CACHE_SIZE */
#ifdef DB_HASH_NELEM
if (dbtype == DB_HASH)
{
ret = db->set_h_nelem(db, DB_HASH_NELEM);
if (ret != 0)
{
ret = BDB_ERR2RET(ret);
goto error;
}
}
#endif /* DB_HASH_NELEM */
/* open BDB map ... */
ret = db->open(db, NULL, path, NULL, dbtype, bdb_open_flags(flags),
DBMMODE);
if (ret != 0)
{
ret = BDB_ERR2RET(ret);
goto error;
}
return ret;
error:
if (db != NULL)
{
(void) db->close(db, 0);
db = NULL;
}
return ret;
}
/*
** SM_BDB_LOOKUP -- lookup a key in BDB, return data if found
**
** Parameters:
** map -- map context
** flags -- flags
** key -- key
** data -- data (output)
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
sm_bdb_lookup(sm_map_P map, uint32_t flags, sm_map_key_P key, sm_map_data_P data)
{
sm_ret_T ret;
#if MTA_USE_PTHREADS
size_t l;
#endif
sm_mapc_P mapc;
DB *db;
DBT db_key, db_data;
SM_IS_MAP(map);
SM_IS_KEY(key);
SM_IS_DATA(data);
mapc = map->sm_map_class;
SM_IS_MAPC(mapc);
ret = SM_SUCCESS;
db = (DB *) map->sm_map_db;
if (db == NULL)
return sm_error_perm(SM_EM_MAP, ENOENT); /* XXX */
if (!SMMAP_IS_FL(map, SMMAP_FL_OPEN))
{
/* map closed but can be reopened? */
if (mapc->sm_mapc_reopenf != NULL)
ret = mapc->sm_mapc_reopenf(map, 0);
else
ret = sm_error_perm(SM_EM_MAP, SM_E_CLOSEDMAP);
if (sm_is_err(ret))
return ret;
}
sm_memzero(&db_key, sizeof(db_key));
sm_memzero(&db_data, sizeof(db_data));
/* XXX WARNING: changes key inplace! */
if (SM_IS_FLAG(flags, SMMAP_FL_LWR_KEY))
sm_str2lower(key);
db_key.size = sm_str_getlen(key);
db_key.data = sm_str_data(key);
#if MTA_USE_PTHREADS
db_data.flags = DB_DBT_USERMEM;
db_data.data = sm_str_data(data);
db_data.ulen = sm_str_getsize(data);
ret = db->get(db, NULL, &db_key, &db_data, 0);
l = db_data.size;
if (ret == DB_BUFFER_SMALL && l < sm_str_getmax(data))
{
if (!sm_is_err(sm_str_resize_data(data, l)))
{
db_data.data = sm_str_data(data);
db_data.ulen = sm_str_getsize(data);
ret = db->get(db, NULL, &db_key, &db_data, 0);
l = db_data.size;
}
}
if (ret == 0 && l < sm_str_getmax(data))
SM_STR_SETLEN(data, db_data.size);
#else /* MTA_USE_PTHREADS */
ret = db->get(db, NULL, &db_key, &db_data, 0);
if (ret == 0 && data != NULL)
{
ret = sm_str_scatn(data, (const char *) db_data.data,
db_data.size);
if (sm_is_err(ret))
return ret;
}
#endif /* MTA_USE_PTHREADS */
return BDB_ERR2RET(ret);
}
/*
** SM_BDB_ADD -- add key/data to BDB
**
** Parameters:
** map -- map context
** key -- key
** data -- data
** flags -- flags
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
sm_bdb_add(sm_map_P map, sm_map_key_P key, sm_map_data_P data, uint flags)
{
sm_ret_T ret;
sm_mapc_P mapc;
DB *db;
DBT db_key, db_data;
SM_IS_MAP(map);
SM_REQUIRE(key != NULL);
SM_REQUIRE(data != NULL);
mapc = map->sm_map_class;
SM_IS_MAPC(mapc);
ret = SM_SUCCESS;
/* this needs to be more sophisticated if more flag values are used! */
if (flags == SMMAP_AFL_UNIQUE)
flags = DB_NOOVERWRITE;
db = (DB *) map->sm_map_db;
if (db == NULL)
return sm_error_perm(SM_EM_MAP, ENOENT); /* XXX */
sm_memzero(&db_key, sizeof(db_key));
sm_memzero(&db_data, sizeof(db_data));
db_key.size = sm_str_getlen(key);
db_key.data = sm_str_data(key);
db_data.size = sm_str_getlen(data);
db_data.data = sm_str_data(data);
ret = db->put(db, NULL, &db_key, &db_data, flags);
return BDB_ERR2RET(ret);
}
/*
** SM_BDB_RM -- remove key/data from BDB
**
** Parameters:
** map -- map context
** key -- key
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
sm_bdb_rm(sm_map_P map, sm_map_key_P key)
{
sm_ret_T ret;
sm_mapc_P mapc;
DB *db;
DBT db_key;
SM_IS_MAP(map);
SM_REQUIRE(key != NULL);
mapc = map->sm_map_class;
SM_IS_MAPC(mapc);
ret = SM_SUCCESS;
db = (DB *) map->sm_map_db;
if (db == NULL)
return sm_error_perm(SM_EM_MAP, ENOENT); /* XXX */
sm_memzero(&db_key, sizeof(db_key));
db_key.size = sm_str_getlen(key);
db_key.data = sm_str_data(key);
ret = db->del(db, NULL, &db_key, 0);
return BDB_ERR2RET(ret);
}
/*
** SM_BDB_CLASS_CREATE -- create BDB map class
**
** Parameters:
** maps -- map system context
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_bdb_class_create(sm_maps_P maps)
{
sm_ret_T ret;
sm_mapc_P mapc;
sm_cstr_P htype;
#define BDB_HASH "hash"
/* add btree? */
ret = SM_SUCCESS;
mapc = NULL;
htype = sm_cstr_scpyn0((const uchar *)BDB_HASH, strlen(BDB_HASH));
if (htype == NULL)
goto error;
ret = sm_mapc_create(maps, htype,
SMMAPC_FL_ALLOC_K|SMMAPC_FL_ALLOC_D|
SMMAPC_FL_FREE_K| SMMAPC_FL_FREE_D|
SMMAPC_FL_GEN_REOPEN|SMMAPC_FL_FILE,
sm_bdb_create,
sm_bdb_open,
sm_bdb_close,
NULL,
sm_bdb_destroy,
sm_bdb_add,
sm_bdb_rm,
sm_bdb_alloc,
sm_bdb_free,
sm_bdb_lookup,
sm_bdb_locate,
sm_bdb_first,
sm_bdb_next,
sm_bdb_setopt,
sm_bdb_getopt,
&mapc);
SM_CSTR_FREE(htype);
return ret;
error:
if (ret == SM_SUCCESS)
ret = sm_error_temp(SM_EM_MAP, ENOMEM);
/* cleanup mapc? */
return ret;
}
syntax highlighted by Code2HTML, v. 0.9.1