#include "sys_defs.h"
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>
#include "msg.h"
#include "mymalloc.h"
#include "htable.h"
#include "iostuff.h"
#include "vstring.h"
#include "myflock.h"
#include "stringops.h"
#include "dict.h"
#include "dict_sdbm.h"
#include "sdbm.h"
typedef struct {
DICT dict;
SDBM *dbm;
char *path;
} DICT_SDBM;
static const char *dict_sdbm_lookup(DICT *dict, const char *name)
{
DICT_SDBM *dict_sdbm = (DICT_SDBM *) dict;
datum dbm_key;
datum dbm_value;
static VSTRING *buf;
const char *result = 0;
dict_errno = 0;
if ((dict->flags & DICT_FLAG_LOCK)
&& myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
msg_fatal("%s: lock dictionary: %m", dict_sdbm->path);
if (dict->flags & DICT_FLAG_TRY1NULL) {
dbm_key.dptr = (void *) name;
dbm_key.dsize = strlen(name) + 1;
dbm_value = sdbm_fetch(dict_sdbm->dbm, dbm_key);
if (dbm_value.dptr != 0) {
dict->flags &= ~DICT_FLAG_TRY0NULL;
result = dbm_value.dptr;
}
}
if (result == 0 && (dict->flags & DICT_FLAG_TRY0NULL)) {
dbm_key.dptr = (void *) name;
dbm_key.dsize = strlen(name);
dbm_value = sdbm_fetch(dict_sdbm->dbm, dbm_key);
if (dbm_value.dptr != 0) {
if (buf == 0)
buf = vstring_alloc(10);
vstring_strncpy(buf, dbm_value.dptr, dbm_value.dsize);
dict->flags &= ~DICT_FLAG_TRY1NULL;
result = vstring_str(buf);
}
}
if ((dict->flags & DICT_FLAG_LOCK)
&& myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
msg_fatal("%s: unlock dictionary: %m", dict_sdbm->path);
return (result);
}
static void dict_sdbm_update(DICT *dict, const char *name, const char *value)
{
DICT_SDBM *dict_sdbm = (DICT_SDBM *) dict;
datum dbm_key;
datum dbm_value;
int status;
dbm_key.dptr = (void *) name;
dbm_value.dptr = (void *) value;
dbm_key.dsize = strlen(name);
dbm_value.dsize = strlen(value);
if ((dict->flags & DICT_FLAG_TRY1NULL)
&& (dict->flags & DICT_FLAG_TRY0NULL)) {
#ifdef DBM_NO_TRAILING_NULL
dict->flags &= ~DICT_FLAG_TRY1NULL;
#else
dict->flags &= ~DICT_FLAG_TRY0NULL;
#endif
}
if (dict->flags & DICT_FLAG_TRY1NULL) {
dbm_key.dsize++;
dbm_value.dsize++;
}
if ((dict->flags & DICT_FLAG_LOCK)
&& myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
msg_fatal("%s: lock dictionary: %m", dict_sdbm->path);
if ((status = sdbm_store(dict_sdbm->dbm, dbm_key, dbm_value,
(dict->flags & DICT_FLAG_DUP_REPLACE) ? DBM_REPLACE : DBM_INSERT)) < 0)
msg_fatal("error writing SDBM database %s: %m", dict_sdbm->path);
if (status) {
if (dict->flags & DICT_FLAG_DUP_IGNORE)
;
else if (dict->flags & DICT_FLAG_DUP_WARN)
msg_warn("%s: duplicate entry: \"%s\"", dict_sdbm->path, name);
else
msg_fatal("%s: duplicate entry: \"%s\"", dict_sdbm->path, name);
}
if ((dict->flags & DICT_FLAG_LOCK)
&& myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
msg_fatal("%s: unlock dictionary: %m", dict_sdbm->path);
}
static int dict_sdbm_delete(DICT *dict, const char *name)
{
DICT_SDBM *dict_sdbm = (DICT_SDBM *) dict;
datum dbm_key;
int status = 1;
int flags = 0;
if ((dict->flags & DICT_FLAG_LOCK)
&& myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
msg_fatal("%s: lock dictionary: %m", dict_sdbm->path);
if (dict->flags & DICT_FLAG_TRY1NULL) {
dbm_key.dptr = (void *) name;
dbm_key.dsize = strlen(name) + 1;
sdbm_clearerr(dict_sdbm->dbm);
if ((status = sdbm_delete(dict_sdbm->dbm, dbm_key)) < 0) {
if (sdbm_error(dict_sdbm->dbm) != 0)
msg_fatal("error deleting from %s: %m", dict_sdbm->path);
status = 1;
} else {
dict->flags &= ~DICT_FLAG_TRY0NULL;
}
}
if (status > 0 && (dict->flags & DICT_FLAG_TRY0NULL)) {
dbm_key.dptr = (void *) name;
dbm_key.dsize = strlen(name);
sdbm_clearerr(dict_sdbm->dbm);
if ((status = sdbm_delete(dict_sdbm->dbm, dbm_key)) < 0) {
if (sdbm_error(dict_sdbm->dbm) != 0)
msg_fatal("error deleting from %s: %m", dict_sdbm->path);
status = 1;
} else {
dict->flags &= ~DICT_FLAG_TRY1NULL;
}
}
if ((dict->flags & DICT_FLAG_LOCK)
&& myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
msg_fatal("%s: unlock dictionary: %m", dict_sdbm->path);
return (status);
}
static int dict_sdbm_sequence(DICT *dict, const int function,
const char **key, const char **value)
{
char *myname = "dict_sdbm_sequence";
DICT_SDBM *dict_sdbm = (DICT_SDBM *) dict;
datum dbm_key;
datum dbm_value;
int status = 0;
static VSTRING *key_buf;
static VSTRING *value_buf;
if ((dict->flags & DICT_FLAG_LOCK)
&& myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
msg_fatal("%s: lock dictionary: %m", dict_sdbm->path);
switch (function) {
case DICT_SEQ_FUN_FIRST:
dbm_key = sdbm_firstkey(dict_sdbm->dbm);
break;
case DICT_SEQ_FUN_NEXT:
dbm_key = sdbm_nextkey(dict_sdbm->dbm);
break;
default:
msg_panic("%s: invalid function: %d", myname, function);
}
if ((dict->flags & DICT_FLAG_LOCK)
&& myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
msg_fatal("%s: unlock dictionary: %m", dict_sdbm->path);
if (dbm_key.dptr != 0 && dbm_key.dsize > 0) {
if (((char *) dbm_key.dptr)[dbm_key.dsize - 1] == 0) {
*key = dbm_key.dptr;
} else {
if (key_buf == 0)
key_buf = vstring_alloc(10);
vstring_strncpy(key_buf, dbm_key.dptr, dbm_key.dsize);
*key = vstring_str(key_buf);
}
dbm_value = sdbm_fetch(dict_sdbm->dbm, dbm_key);
if (dbm_value.dptr != 0 && dbm_value.dsize > 0) {
if (((char *) dbm_value.dptr)[dbm_value.dsize - 1] == 0) {
*value = dbm_value.dptr;
} else {
if (value_buf == 0)
value_buf = vstring_alloc(10);
vstring_strncpy(value_buf, dbm_value.dptr, dbm_value.dsize);
*value = vstring_str(value_buf);
}
} else {
if (sdbm_error(dict_sdbm->dbm))
msg_fatal("error seeking %s: %m", dict_sdbm->path);
return (1);
}
} else {
if (sdbm_error(dict_sdbm->dbm))
msg_fatal("error seeking %s: %m", dict_sdbm->path);
return (1);
}
return (0);
}
static void dict_sdbm_close(DICT *dict)
{
DICT_SDBM *dict_sdbm = (DICT_SDBM *) dict;
sdbm_close(dict_sdbm->dbm);
myfree(dict_sdbm->path);
myfree((char *) dict_sdbm);
}
DICT *dict_sdbm_open(const char *path, int open_flags, int dict_flags)
{
DICT_SDBM *dict_sdbm;
struct stat st;
SDBM *dbm;
char *dbm_path;
int lock_fd;
if (dict_flags & DICT_FLAG_LOCK) {
dbm_path = concatenate(path, ".pag", (char *) 0);
if ((lock_fd = open(dbm_path, open_flags, 0644)) < 0)
msg_fatal("open database %s: %m", dbm_path);
if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
msg_fatal("shared-lock database %s for open: %m", dbm_path);
}
if ((dbm = sdbm_open((char *) path, open_flags, 0644)) == 0)
msg_fatal("open database %s.{dir,pag}: %m", path);
if (dict_flags & DICT_FLAG_LOCK) {
if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
msg_fatal("unlock database %s for open: %m", dbm_path);
if (close(lock_fd) < 0)
msg_fatal("close database %s: %m", dbm_path);
myfree(dbm_path);
}
dict_sdbm = (DICT_SDBM *) mymalloc(sizeof(*dict_sdbm));
dict_sdbm->dict.lookup = dict_sdbm_lookup;
dict_sdbm->dict.update = dict_sdbm_update;
dict_sdbm->dict.delete = dict_sdbm_delete;
dict_sdbm->dict.sequence = dict_sdbm_sequence;
dict_sdbm->dict.close = dict_sdbm_close;
dict_sdbm->dict.lock_fd = sdbm_dirfno(dbm);
dict_sdbm->dict.stat_fd = sdbm_pagfno(dbm);
if (fstat(dict_sdbm->dict.stat_fd, &st) < 0)
msg_fatal("dict_sdbm_open: fstat: %m");
dict_sdbm->dict.mtime = st.st_mtime;
close_on_exec(sdbm_pagfno(dbm), CLOSE_ON_EXEC);
close_on_exec(sdbm_dirfno(dbm), CLOSE_ON_EXEC);
dict_sdbm->dict.flags = dict_flags | DICT_FLAG_FIXED;
if ((dict_flags & (DICT_FLAG_TRY0NULL | DICT_FLAG_TRY1NULL)) == 0)
dict_sdbm->dict.flags |= (DICT_FLAG_TRY0NULL | DICT_FLAG_TRY1NULL);
dict_sdbm->dbm = dbm;
dict_sdbm->path = mystrdup(path);
return (&dict_sdbm->dict);
}