/* * 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; }