/*
* 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: map.c,v 1.52 2007/01/23 17:54:57 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/stat.h"
#if MTA_USE_PTHREADS
#define SMMAP_WR_LOCK(map) do { \
if (SM_IS_FLAG(mapc->sm_mapc_flags, \
SMMAPC_FL_LCK_WR|SMMAPC_FL_LCK_FULL)) \
{ \
r = pthread_rwlock_wrlock(&map->sm_map_rwlock); \
if (r != 0) \
return sm_error_perm(SM_EM_MAP, r); \
} \
} while (0)
#define SMMAP_RD_LOCK(map) do { \
if (SM_IS_FLAG(mapc->sm_mapc_flags, SMMAPC_FL_LCK_FULL)) { \
r = pthread_rwlock_wrlock(&map->sm_map_rwlock); \
if (r != 0) \
return sm_error_perm(SM_EM_MAP, r); \
} \
else if (SM_IS_FLAG(mapc->sm_mapc_flags, SMMAPC_FL_LCK_WR)) { \
r = pthread_rwlock_rdlock(&map->sm_map_rwlock); \
if (r != 0) \
return sm_error_perm(SM_EM_MAP, r); \
} \
} while (0)
#define SMMAP_WR_UNLOCK(map) do { \
if (SM_IS_FLAG(mapc->sm_mapc_flags, \
SMMAPC_FL_LCK_WR|SMMAPC_FL_LCK_FULL)) \
{ \
r = pthread_rwlock_unlock(&map->sm_map_rwlock); \
if (r != 0) \
return sm_error_perm(SM_EM_MAP, r); \
} \
} while (0)
#define SMMAP_RD_UNLOCK(map) SMMAP_WR_UNLOCK(map)
#else /* MTA_USE_PTHREADS */
#define SMMAP_WR_LOCK(map)
#define SMMAP_WR_UNLOCK(map)
#define SMMAP_RD_LOCK(map)
#define SMMAP_RD_UNLOCK(map)
#endif /* MTA_USE_PTHREADS */
/*
** SM_MAP_DESTROY -- destroy a map
**
** Parameters:
** map -- map context
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_map_destroy(sm_map_P map, uint32_t flags)
{
sm_ret_T ret;
sm_mapc_P mapc;
if (NULL == map)
return SM_SUCCESS;
SM_IS_MAP(map);
mapc = map->sm_map_class;
SM_IS_MAPC(mapc);
if (mapc->sm_mapc_destroyf != NULL)
ret = mapc->sm_mapc_destroyf(map, flags);
else
ret = SM_SUCCESS;
#if MTA_USE_PTHREADS
(void) pthread_rwlock_destroy(&map->sm_map_rwlock);
#endif
sm_free_size(map, sizeof(*map));
return SM_SUCCESS;
}
/*
** SM_MAP_CREATE -- create map
**
** Parameters:
** maps -- map system context
** type -- type of map
** flags -- flags for map
** pmap -- pointer to map (output)
** Note: The following assertions must hold:
** pmap != NULL && *pmap == NULL
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_map_create(sm_maps_P maps, sm_cstr_P type, uint32_t flags, sm_map_P *pmap)
{
sm_ret_T ret;
sm_map_P map;
sm_mapc_P mapc;
#if MTA_USE_PTHREADS
int r;
#endif
SM_REQUIRE(pmap != NULL);
SM_REQUIRE(*pmap == NULL);
ret = sm_maps_find(maps, type, &mapc);
if (sm_is_err(ret))
return ret;
map = (sm_map_P) sm_zalloc(sizeof(*map));
if (NULL == map)
return sm_error_temp(SM_EM_MAP, ENOMEM);
/* can be overridden in map specific create function */
map->sm_map_caps = SMMAP_CAPS_LTALL;
if (mapc->sm_mapc_createf != NULL) {
ret = mapc->sm_mapc_createf(mapc, type, flags, map);
if (sm_is_err(ret))
goto error;
}
map->sm_map_type = SM_CSTR_DUP(type);
map->sm_map_class = mapc;
#if MTA_USE_PTHREADS
r = pthread_rwlock_init(&map->sm_map_rwlock, NULL);
if (r != 0) {
sm_free_size(map, sizeof(*map));
return sm_error_perm(SM_EM_MAP, r);
}
#endif /* MTA_USE_PTHREADS */
map->sm_map_flags = SMMAP_FL_CREATED;
#if SM_MAP_CHECK
map->sm_magic = SM_MAP_MAGIC;
#endif
*pmap = map;
return SM_SUCCESS;
error:
sm_free_size(map, sizeof(*map)); /* XXX more data to free? */
*pmap = NULL;
return ret;
}
/*
** SM_MAP_OPEN -- open map
** XXX more parameters...
**
** Parameters:
** maps -- map system context
** name -- name of map
** type -- type of map
** flags -- flags for map
** XXX define what these flags do... don't just pass them through
** maybe compare sm_io_open() or sm8 map functions; in the latter
** parseargs() defines the flags, not open().
** maybe this should be "mode": open for read/write/create/excl.
** add another function that sets flags for the map behavior, e.g.,
** map to lower case, include trailing '\0', ...
** path -- path for map (NOT copied, must be persistent in caller)
** pmap -- pointer to map (output)
** Note: The following assertions must hold:
** pmap != NULL
** *pmap == NULL iff map_create() has not been called.
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_map_open(sm_maps_P maps, const sm_cstr_P name, const sm_cstr_P type, uint32_t flags, const char *path, int mode, sm_map_P *pmap, ...)
{
sm_ret_T ret;
sm_map_P map;
sm_mapc_P mapc;
struct stat sb;
va_list ap;
SM_IS_MAPS(maps);
SM_REQUIRE(pmap != NULL);
ret = sm_maps_find(maps, type, &mapc);
if (sm_is_err(ret))
return ret;
if (*pmap == NULL) {
ret = sm_map_create(maps, type, flags, pmap);
if (sm_is_err(ret))
return ret;
}
map = *pmap;
SM_IS_MAP(map);
SM_REQUIRE(SMMAP_IS_FL(map, SMMAP_FL_CREATED));
/* call open function ... */
if (mapc->sm_mapc_openf != NULL) {
va_start(ap, pmap);
ret = mapc->sm_mapc_openf(mapc, type, flags, path, mode, map, ap);
va_end(ap);
if (sm_is_err(ret))
goto error;
}
#if 0
else
/* XXX error? */;
#endif
map->sm_map_openflags = flags;
map->sm_map_mode = mode;
map->sm_map_name = SM_CSTR_DUP(name);
map->sm_map_path = path; /* XXX NOT COPIED!!! */
if (path != NULL && stat((const char *)path, &sb) == 0) {
map->sm_map_mtime = sb.st_mtime;
map->sm_map_ino = sb.st_ino;
}
else {
map->sm_map_mtime = 0;
map->sm_map_ino = 0;
}
ret = sm_mapname_add(maps, map);
if (sm_is_err(ret))
goto error;
#if 0
map->sm_map_app_ctx = NULL;
map->sm_map_key_allocf = sm_map_dbt_alloc;
map->sm_map_key_freef = sm_map_dbt_free;
map->sm_map_data_allocf = sm_map_dbt_alloc;
map->sm_map_data_freef = sm_map_dbt_free;
#endif /* 0 */
SMMAP_SET_FL(map, SMMAP_FL_OPEN);
return SM_SUCCESS;
error:
sm_free_size(map, sizeof(*map)); /* XXX more data to free? */
*pmap = NULL;
return ret;
}
/*
** SM_MAP_CLOSE -- close map
** XXX more parameters...
**
** Parameters:
** map -- map
** flags -- flags
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_map_close(sm_map_P map, uint32_t flags)
{
sm_ret_T ret;
sm_mapc_P mapc;
if (NULL == map)
return SM_SUCCESS;
SM_IS_MAP(map);
mapc = map->sm_map_class;
SM_IS_MAPC(mapc);
SMMAP_SET_FL(map, SMMAP_FL_CLOSING);
ret = sm_mapname_rm(mapc->sm_mapc_maps, map);
/* ignore result? */
if (mapc->sm_mapc_closef != NULL && SMMAP_IS_FL(map, SMMAP_FL_OPEN))
ret = mapc->sm_mapc_closef(map, flags);
else
ret = SM_SUCCESS;
SMMAP_CLR_FL(map, SMMAP_FL_OPEN);
#if SM_MAP_CHECK
map->sm_magic = SM_MAGIC_NULL;
#endif
SMMAP_SET_FL(map, SMMAP_FL_CLOSED);
SMMAP_CLR_FL(map, SMMAP_FL_CLOSING);
SM_CSTR_FREE(map->sm_map_name);
SM_CSTR_FREE(map->sm_map_type);
/* really? */
sm_free_size(map, sizeof(*map));
return ret;
}
/*
** SM_MAP_REOPEN -- reopen map
**
** Parameters:
** map -- map to reopen
** flags -- flags (unused right now...)
**
** Returns:
** usual sm_error code
**
** How does this deal with errors? In which state will the map be?
*/
sm_ret_T
sm_map_reopen(sm_map_P map, uint32_t flags, ...)
{
sm_ret_T ret;
sm_mapc_P mapc;
#if MTA_USE_PTHREADS
int r;
#endif
struct stat sb;
va_list ap;
SM_IS_MAP(map);
ret = SM_SUCCESS;
mapc = map->sm_map_class;
SMMAP_WR_LOCK(map);
if (SM_IS_FLAG(mapc->sm_mapc_flags, SMMAPC_FL_GEN_REOPEN)
&&
/* did the file change? */
(map->sm_map_path == NULL
|| stat(map->sm_map_path, &sb) != 0
|| map->sm_map_mtime != sb.st_mtime
|| map->sm_map_ino != sb.st_ino))
{
if (mapc->sm_mapc_closef != NULL && SMMAP_IS_FL(map, SMMAP_FL_OPEN)) {
SMMAP_SET_FL(map, SMMAP_FL_CLOSING);
ret = mapc->sm_mapc_closef(map, 0);
SMMAP_CLR_FL(map, SMMAP_FL_OPEN);
SMMAP_CLR_FL(map, SMMAP_FL_CLOSING);
SMMAP_SET_FL(map, SMMAP_FL_CLOSED);
}
/* call create function ... */
if (!sm_is_err(ret) && mapc->sm_mapc_createf != NULL) {
ret = mapc->sm_mapc_createf(mapc, map->sm_map_type
, map->sm_map_openflags, map);
}
if (!sm_is_err(ret))
ret = sm_map_setopts(map);
/* call open function ... */
if (!sm_is_err(ret) && mapc->sm_mapc_openf != NULL) {
va_start(ap, flags);
ret = mapc->sm_mapc_openf(mapc, map->sm_map_type
, map->sm_map_openflags, map->sm_map_path, map->sm_map_mode
, map, ap);
va_end(ap);
if (sm_is_success(ret)) {
SMMAP_CLR_FL(map, SMMAP_FL_CLOSED);
SMMAP_SET_FL(map, SMMAP_FL_OPEN);
}
/* XXX ap? How to "remember" these and how to create a va_list? */
}
#if 0
else
/* XXX error? */;
#endif
}
else if (mapc->sm_mapc_reopenf != NULL) {
SMMAP_SET_FL(map, SMMAP_FL_CLOSING);
ret = mapc->sm_mapc_reopenf(map, 0);
SMMAP_CLR_FL(map, SMMAP_FL_CLOSING);
if (sm_is_success(ret))
SMMAP_SET_FL(map, SMMAP_FL_OPEN);
else
SMMAP_SET_FL(map, SMMAP_FL_CLOSED);
}
SMMAP_WR_UNLOCK(map);
return ret;
}
/*
** MAP_LOOKUP -- lookup value
**
** Parameters:
** map -- map
** flags -- flags
** key -- key
** data -- data
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_map_lookup(sm_map_P map, uint32_t flags, sm_map_key_P key, sm_map_data_P data)
{
sm_ret_T ret;
sm_mapc_P mapc;
#if MTA_USE_PTHREADS
int r;
#endif
if (NULL == map)
return sm_error_perm(SM_EM_MAP, SM_E_NOMAP); /* XXX */
SM_IS_MAP(map);
mapc = map->sm_map_class;
SM_IS_MAPC(mapc);
if (!SMMAP_IS_FL(map, SMMAP_FL_OPEN)) {
/* map closed but can be reopened? */
if (!SMMAP_IS_CAPS(map, SMMAP_CAPS_DYNAMIC) &&
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;
}
/*
** Only call lookup function if no capabilities are specified
** or the capabilities are available.
*/
if (NULL == mapc->sm_mapc_lookupf)
ret = sm_error_perm(SM_EM_MAP, SM_E_NOTIMPL);
else if ((flags & SMMAP_CAPS_LTMASK) == SMMAP_FL_NONE
|| SMMAP_LT_M_CAPS(map, flags))
{
SMMAP_RD_LOCK(map);
ret = mapc->sm_mapc_lookupf(map, flags, key, data);
SMMAP_RD_UNLOCK(map);
}
else
ret = sm_error_perm(SM_EM_MAP, SM_E_UNAVAIL);
return ret;
}
/*
** SM_MAP_ADD -- add data for key
**
** Parameters:
** map -- map
** key -- key
** data -- data
** flags -- flags
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_map_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;
#if MTA_USE_PTHREADS
int r;
#endif
if (NULL == map)
return sm_error_perm(SM_EM_MAP, SM_E_NOMAP);
SM_IS_MAP(map);
mapc = map->sm_map_class;
SM_IS_MAPC(mapc);
if (mapc->sm_mapc_addf != NULL) {
SMMAP_WR_LOCK(map);
ret = mapc->sm_mapc_addf(map, key, data, flags);
SMMAP_WR_UNLOCK(map);
}
else
ret = sm_error_perm(SM_EM_MAP, SM_E_NOTIMPL);
return ret;
}
/*
** SM_MAP_RM -- remove data for key
**
** Parameters:
** map -- map
** key -- key
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_map_rm(sm_map_P map, sm_map_key_P key)
{
sm_ret_T ret;
sm_mapc_P mapc;
#if MTA_USE_PTHREADS
int r;
#endif
if (NULL == map)
return sm_error_perm(SM_EM_MAP, SM_E_NOMAP);
SM_IS_MAP(map);
mapc = map->sm_map_class;
SM_IS_MAPC(mapc);
if (mapc->sm_mapc_rmf != NULL) {
SMMAP_WR_LOCK(map);
ret = mapc->sm_mapc_rmf(map, key);
SMMAP_WR_UNLOCK(map);
}
else
ret = sm_error_perm(SM_EM_MAP, SM_E_NOTIMPL);
return ret;
}
syntax highlighted by Code2HTML, v. 0.9.1