431 lines
9.7 KiB
C
431 lines
9.7 KiB
C
#include <stdio.h>
|
|
#include <limits.h>
|
|
#include <fcntl.h>
|
|
#include <abi_mutex.h>
|
|
#include <time.h>
|
|
#include <ctype.h>
|
|
#include <malloc.h>
|
|
#include <alloca.h>
|
|
#include <mdbm.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/param.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <ns_api.h>
|
|
#include <ns_daemon.h>
|
|
|
|
/* This cachesize parameter is 2^n pages */
|
|
#define NSD_DEFAULT_CACHESIZE 4
|
|
|
|
nsd_maps_t *__nsd_maps;
|
|
|
|
/*
|
|
** This routine is called by the database code when a datum
|
|
** will not fit on a page to determine if the requested item
|
|
** can be deleted. It is called three times with increasing
|
|
** priority values. On the first pass we throw away items which
|
|
** have timed out. On the second pass items which represent
|
|
** failed attempts. And, finally, we throw anything away on
|
|
** the third pass.
|
|
*/
|
|
/* ARGSUSED */
|
|
static int
|
|
nsd_cache_shake(MDBM *db, datum key, datum value, void *pri)
|
|
{
|
|
ns_cache_t nc;
|
|
int i;
|
|
time_t now;
|
|
|
|
memcpy(&nc, value.dptr, sizeof(nc));
|
|
i = (int)pri;
|
|
time(&now);
|
|
if (i == 0) {
|
|
if (now > nc.c_timeout) {
|
|
return 1;
|
|
}
|
|
} else if (i == 1) {
|
|
if (nc.c_status != NS_SUCCESS) {
|
|
return 1;
|
|
}
|
|
} else {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** This is the shake routine for the cache files. We just close off
|
|
** maps that have not been touched lately.
|
|
*/
|
|
void
|
|
nsd_map_shake(int priority, time_t now)
|
|
{
|
|
nsd_maps_t *mp;
|
|
|
|
now -= (10 - priority) * 30;
|
|
for (mp = __nsd_maps; mp; mp = mp->m_next) {
|
|
if (mp->m_map && (mp->m_touched < now)) {
|
|
mdbm_close(mp->m_map);
|
|
mp->m_map = (MDBM *)0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
** This will open the cache file for a given table and set the
|
|
** permission according to the attributes.
|
|
*/
|
|
int
|
|
nsd_map_open(nsd_file_t *file)
|
|
{
|
|
long mode, owner, group, pagesize, cachesize;
|
|
int len;
|
|
char filename[MAXPATHLEN];
|
|
|
|
if (! file) {
|
|
return NSD_ERROR;
|
|
}
|
|
if (! file->f_map) {
|
|
return NSD_ERROR;
|
|
}
|
|
if (file->f_map->m_map) {
|
|
time(&file->f_map->m_touched);
|
|
return NSD_OK;
|
|
}
|
|
|
|
/*
|
|
** Lookup default values for cache parameters.
|
|
*/
|
|
mode = nsd_attr_fetch_long(file->f_attrs, "mode", 8, 0644);
|
|
owner = nsd_attr_fetch_long(file->f_attrs, "owner", 10, geteuid());
|
|
group = nsd_attr_fetch_long(file->f_attrs, "group", 10, getegid());
|
|
pagesize = nsd_attr_fetch_long(file->f_attrs, "pagesize", 10, 0);
|
|
cachesize = nsd_attr_fetch_long(file->f_attrs, "cachesize", 10,
|
|
NSD_DEFAULT_CACHESIZE);
|
|
|
|
/*
|
|
** Build filename.
|
|
*/
|
|
if (! file->f_map->m_file) {
|
|
len = nsd_cat(filename, MAXPATHLEN, 3, NS_CACHE_DIR, "/",
|
|
file->f_map->m_name);
|
|
file->f_map->m_file = (char *)nsd_malloc(len + 1);
|
|
if (! file->f_map->m_file) {
|
|
nsd_logprintf(NSD_LOG_RESOURCE,
|
|
"nsd_map_open: failed malloc\n");
|
|
return NSD_ERROR;
|
|
}
|
|
memcpy(file->f_map->m_file, filename, len);
|
|
file->f_map->m_file[len] = (char)0;
|
|
}
|
|
|
|
/*
|
|
** Check numeric attributes
|
|
*/
|
|
if (pagesize && ((pagesize < 8) || (pagesize > 16))) {
|
|
nsd_logprintf(NSD_LOG_RESOURCE,
|
|
"nsd_map_open: invalid pagesize %ld, valid range 0,8-16\n",
|
|
pagesize);
|
|
pagesize=0;
|
|
}
|
|
|
|
file->f_map->m_map =
|
|
nsd_mdbm_open(file->f_map->m_file,
|
|
O_RDWR | O_CREAT | MDBM_ALLOC_SPACE |
|
|
MDBM_MEMCHECK,
|
|
mode, pagesize);
|
|
if (! file->f_map->m_map) {
|
|
nsd_logprintf(NSD_LOG_OPER,
|
|
"nsd_map_open: failed to open cache file %s\n",
|
|
file->f_map->m_file);
|
|
return NSD_ERROR;
|
|
}
|
|
|
|
/*
|
|
** Extend the cache to the default size, and make sure that it
|
|
** is aligned to a time_t. If it is fixed size we can also close
|
|
** the file since it will never need to be remapped.
|
|
*/
|
|
if (cachesize) {
|
|
if (cachesize < 3 || cachesize > 16) {
|
|
nsd_logprintf(NSD_LOG_RESOURCE,
|
|
"nsd_map_open: invalid cachesize %ld, valid range 0,3-16\n",
|
|
cachesize);
|
|
cachesize=NSD_DEFAULT_CACHESIZE;
|
|
}
|
|
|
|
if ((mdbm_pre_split(file->f_map->m_map, cachesize,
|
|
MDBM_ALLOC_SPACE) < 0 ) &&
|
|
(errno != EEXIST)) {
|
|
if (!(file->f_map->m_flags & NSD_MAP_ALLOC)) {
|
|
nsd_logprintf(NSD_LOG_RESOURCE,
|
|
"failed to allocate space for %s cache\n",
|
|
file->f_map->m_name);
|
|
file->f_map->m_flags |= NSD_MAP_ALLOC;
|
|
}
|
|
/*
|
|
** If we cant allocate the space, then delete it.
|
|
** we dont want it sitting around for others to use
|
|
** and have it act as a normal (growing) mdbm file
|
|
*/
|
|
mdbm_invalidate(file->f_map->m_map);
|
|
mdbm_close(file->f_map->m_map);
|
|
file->f_map->m_map = (MDBM *)0;
|
|
unlink(file->f_map->m_file);
|
|
return NSD_ERROR;
|
|
}
|
|
file->f_map->m_flags &= ~NSD_MAP_ALLOC;
|
|
mdbm_limit_size(file->f_map->m_map, cachesize, nsd_cache_shake);
|
|
}
|
|
|
|
/*
|
|
** Fix permissions. We do this everytime since these may be
|
|
** different than when the file was first created.
|
|
*/
|
|
chown(file->f_map->m_file, (uid_t)owner, (gid_t)group);
|
|
chmod(file->f_map->m_file, (mode_t)mode & 0666);
|
|
|
|
time(&file->f_map->m_touched);
|
|
return NSD_OK;
|
|
}
|
|
|
|
/*
|
|
** This just searches through a map list until it gets a matching entry.
|
|
*/
|
|
nsd_maps_t *
|
|
nsd_map_get(char *map)
|
|
{
|
|
nsd_maps_t *nm;
|
|
int len;
|
|
|
|
for (nm = __nsd_maps; nm; nm = nm->m_next) {
|
|
if (strcasecmp(map, nm->m_name) == 0) {
|
|
return nm;
|
|
}
|
|
}
|
|
|
|
len = strlen(map);
|
|
nm = (nsd_maps_t *)nsd_calloc(1, sizeof(nsd_maps_t) + len);
|
|
if (! nm) {
|
|
nsd_logprintf(NSD_LOG_RESOURCE, "nsd_map_get: malloc failed\n");
|
|
return nm;
|
|
}
|
|
|
|
memcpy(nm->m_name, map, len);
|
|
nm->m_name[len] = 0;
|
|
|
|
nm->m_next = __nsd_maps;
|
|
__nsd_maps = nm;
|
|
|
|
return nm;
|
|
}
|
|
|
|
/*
|
|
** This will update a cache entry for this element. If we had a
|
|
** successful lookup then we will always update the cache. If we
|
|
** had some sort of an error then we will only update the cache if
|
|
** the key did not exist or has timed out.
|
|
*/
|
|
int
|
|
nsd_map_update(nsd_file_t *rq)
|
|
{
|
|
char *buf;
|
|
ns_cache_t *cp;
|
|
kvpair kv;
|
|
time_t now, then;
|
|
long timeout;
|
|
int status;
|
|
int pagesize;
|
|
char *val;
|
|
|
|
nsd_logprintf(NSD_LOG_LOW, "nsd_map_update:\n");
|
|
|
|
timeout = nsd_attr_fetch_long(rq->f_attrs, "timeout", 10, 300);
|
|
if (rq->f_status != NS_SUCCESS) {
|
|
timeout = nsd_attr_fetch_long(rq->f_attrs, "negative_timeout",
|
|
10, timeout);
|
|
}
|
|
if (timeout < 0) {
|
|
timeout=0;
|
|
}
|
|
|
|
if (! timeout || ! rq->f_map) {
|
|
return NSD_OK;
|
|
}
|
|
|
|
kv.key.dptr = rq->f_name;
|
|
kv.key.dsize = rq->f_namelen;
|
|
|
|
if (nsd_map_open(rq) != NSD_OK) {
|
|
nsd_logprintf(NSD_LOG_MIN, "\tcould not open map\n");
|
|
return NSD_ERROR;
|
|
}
|
|
|
|
pagesize=MDBM_PAGE_SIZE(rq->f_map->m_map);
|
|
val=alloca(pagesize);
|
|
kv.val.dptr=val;
|
|
kv.val.dsize=pagesize;
|
|
|
|
time(&now);
|
|
|
|
if ((rq->f_status != NS_SUCCESS) && (rq->f_status != NS_NOTFOUND)) {
|
|
if (_Mdbm_invalid(rq->f_map->m_map)) {
|
|
mdbm_close(rq->f_map->m_map);
|
|
rq->f_map->m_map = (MDBM *)0;
|
|
if (nsd_map_open(rq) != NSD_OK) {
|
|
nsd_logprintf(NSD_LOG_MIN,
|
|
"\tcould not reopen map\n");
|
|
return NSD_ERROR;
|
|
}
|
|
}
|
|
kv.val = mdbm_fetch(rq->f_map->m_map, kv);
|
|
if (!kv.val.dptr && (errno == EDEADLK ||
|
|
errno == EBADF)) {
|
|
nsd_logprintf(NSD_LOG_OPER,
|
|
"MDBM corruption detected, flushing %s\n",
|
|
rq->f_map->m_name);
|
|
if (nsd_map_flush(rq) != NSD_OK) {
|
|
return NSD_ERROR;
|
|
}
|
|
kv.val.dptr=val;
|
|
kv.val.dsize=pagesize;
|
|
kv.val = mdbm_fetch(rq->f_map->m_map, kv);
|
|
}
|
|
|
|
if (kv.val.dptr) {
|
|
GETLONG(then, kv.val.dptr);
|
|
status = *kv.val.dptr;
|
|
if ((then > now) && (status == NS_SUCCESS)) {
|
|
nsd_logprintf(NSD_LOG_LOW,
|
|
"Valid entry already exists.\n");
|
|
return NSD_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Get space on the stack, then align a pointer to the next long.
|
|
*/
|
|
buf = alloca(rq->f_used + 8);
|
|
cp = (ns_cache_t *)(buf + (sizeof(time_t) -
|
|
((int)buf % sizeof(time_t))));
|
|
|
|
cp->c_timeout = now + timeout;
|
|
cp->c_mtime = now;
|
|
cp->c_status = (char)rq->f_status;
|
|
if (rq->f_data) {
|
|
memcpy(cp->c_data, rq->f_data, rq->f_used);
|
|
}
|
|
|
|
kv.val.dptr = (char *)cp;
|
|
kv.val.dsize = rq->f_used + (2 * sizeof(time_t)) + sizeof(char);
|
|
if (_Mdbm_invalid(rq->f_map->m_map)) {
|
|
mdbm_close(rq->f_map->m_map);
|
|
rq->f_map->m_map = (MDBM *)0;
|
|
if (nsd_map_open(rq) != NSD_OK) {
|
|
nsd_logprintf(NSD_LOG_MIN, "\tcould not reopen map\n");
|
|
return NSD_ERROR;
|
|
}
|
|
}
|
|
|
|
if (mdbm_store(rq->f_map->m_map, kv.key, kv.val, MDBM_REPLACE) < 0) {
|
|
if (errno == EDEADLK || errno == EBADF) {
|
|
nsd_logprintf(NSD_LOG_OPER,
|
|
"MDBM corruption detected, flushing %s\n",
|
|
rq->f_map->m_name);
|
|
if (nsd_map_flush(rq) != NSD_OK) {
|
|
return NSD_ERROR;
|
|
}
|
|
if (mdbm_store(rq->f_map->m_map, kv.key, kv.val,
|
|
MDBM_REPLACE) < 0) {
|
|
nsd_logprintf(NSD_LOG_OPER,
|
|
"Failed 2nd store: %s\n",
|
|
strerror(errno));
|
|
}
|
|
}
|
|
nsd_logprintf(NSD_LOG_LOW, "Failed store: %s\n",
|
|
strerror(errno));
|
|
}
|
|
|
|
return NSD_OK;
|
|
}
|
|
|
|
/*
|
|
** This routine will free up all the space for a given list of maps, and
|
|
** all of their children.
|
|
*/
|
|
void
|
|
nsd_map_close_all(void)
|
|
{
|
|
nsd_maps_t *nm;
|
|
|
|
for (nm = __nsd_maps; nm; nm = nm->m_next) {
|
|
if (nm->m_map) {
|
|
mdbm_close(nm->m_map);
|
|
nm->m_map = (MDBM *)0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nsd_map_delete(nsd_maps_t *map, char *string, int len)
|
|
{
|
|
datum key;
|
|
|
|
key.dptr = string;
|
|
key.dsize = len;
|
|
|
|
mdbm_delete(map->m_map, key);
|
|
}
|
|
|
|
int
|
|
nsd_map_remove(nsd_file_t *rq)
|
|
{
|
|
if (! rq) {
|
|
return NSD_ERROR;
|
|
}
|
|
|
|
if (nsd_map_open(rq) != NSD_OK) {
|
|
return NSD_ERROR;
|
|
}
|
|
|
|
nsd_map_delete(rq->f_map, rq->f_name, rq->f_namelen);
|
|
|
|
return NSD_OK;
|
|
}
|
|
|
|
int
|
|
nsd_map_flush(nsd_file_t *rq)
|
|
{
|
|
nsd_logprintf(NSD_LOG_MIN, "Enterring nsd_map_flush:\n");
|
|
|
|
if (! rq) {
|
|
return NSD_ERROR;
|
|
}
|
|
|
|
if (nsd_map_open(rq) != NSD_OK) {
|
|
return NSD_ERROR;
|
|
}
|
|
|
|
nsd_logprintf(NSD_LOG_LOW, "\tflushing map: %s\n", rq->f_map->m_file);
|
|
mdbm_invalidate(rq->f_map->m_map);
|
|
mdbm_close(rq->f_map->m_map);
|
|
rq->f_map->m_map = (MDBM *)0;
|
|
|
|
return (nsd_map_open(rq));
|
|
}
|
|
|
|
int
|
|
nsd_map_unlink(nsd_file_t *rq)
|
|
{
|
|
nsd_logprintf(NSD_LOG_LOW, "\tFlushing and unlinking map: %s\n",
|
|
rq->f_map->m_file);
|
|
mdbm_invalidate(rq->f_map->m_map);
|
|
mdbm_close(rq->f_map->m_map);
|
|
rq->f_map->m_map = (MDBM *)0;
|
|
unlink(rq->f_map->m_file);
|
|
return NSD_OK;
|
|
}
|