#include "am.h"
#include <regex.h>
#define NKVHASH (1 << 2)
static char wildcard[] = "*";
#define MAPC_DFLT 0x000
#define MAPC_NONE 0x001
#define MAPC_INC 0x002
#define MAPC_ROOT 0x004
#define MAPC_ALL 0x010
#define MAPC_RE 0x020
#define MAPC_ISRE(m) ((m)->alloc == MAPC_RE)
#define MAPC_CACHE_MASK 0x0ff
#define MAPC_SYNC 0x100
static struct opt_tab mapc_opt[] = {
{ "all", MAPC_ALL },
{ "default", MAPC_DFLT },
{ "inc", MAPC_INC },
{ "mapdefault", MAPC_DFLT },
{ "none", MAPC_NONE },
{ "re", MAPC_RE },
{ "regexp", MAPC_RE },
{ "sync", MAPC_SYNC },
{ 0, 0 }
};
#define MREC_FULL 2
#define MREC_PART 1
#define MREC_NONE 0
typedef void add_fn(mnt_map *, char *, char *);
typedef int init_fn(char *, time_t *);
typedef int search_fn(mnt_map *, char *, char *, char **, time_t *);
typedef int reload_fn(mnt_map *, char *, add_fn *);
typedef int mtime_fn(char *, time_t *);
static void mapc_sync(mnt_map *);
typedef struct map_type map_type;
struct map_type {
char *name;
init_fn *init;
reload_fn *reload;
search_fn *search;
mtime_fn *mtime;
int def_alloc;
};
typedef struct kv kv;
struct kv {
kv *next;
char *key;
char *val;
};
struct mnt_map {
qelem hdr;
int refc;
short flags;
short alloc;
time_t modify;
char *map_name;
char *wildcard;
reload_fn *reload;
search_fn *search;
mtime_fn *mtime;
kv *kvhash[NKVHASH];
};
static mnt_map *root_map;
extern qelem map_list_head;
qelem map_list_head = { &map_list_head, &map_list_head };
static int root_init(char *, time_t *);
extern int file_init(char *, time_t *);
extern int file_reload(mnt_map *, char *, add_fn *);
extern int file_search(mnt_map *, char *, char *, char **, time_t *);
extern int file_mtime(char *, time_t *);
extern int nis_init(char *, time_t *);
extern int nis_reload(mnt_map *, char *, add_fn *);
extern int nis_search(mnt_map *, char *, char *, char **, time_t *);
#define nis_mtime nis_init
#ifdef HAS_NDBM_MAPS
extern int ndbm_init(char *, time_t *);
extern int ndbm_search(mnt_map *, char *, charo *, char **, time_t *);
#define ndbm_mtime ndbm_init
#endif
extern int passwd_init(char *, time_t *);
extern int passwd_search(mnt_map *, char *, char *, char **, time_t *);
extern int union_init(char *, time_t *);
extern int union_search(mnt_map *, char *, char *, char **, time_t *);
extern int union_reload(mnt_map *, char *, add_fn *);
static int error_init(char *, time_t *);
static int error_reload(mnt_map *, char *, add_fn *);
static int error_search(mnt_map *, char *, char *, char **, time_t *);
static int error_mtime(char *, time_t *);
static map_type maptypes[] = {
{ "root", root_init, error_reload, error_search, error_mtime, MAPC_ROOT },
{ "passwd", passwd_init, error_reload, passwd_search, error_mtime, MAPC_INC },
{ "union", union_init, union_reload, union_search, error_mtime, MAPC_ALL },
{ "nis", nis_init, nis_reload, nis_search, nis_mtime, MAPC_INC },
#ifdef HAS_NDBM_MAPS
{ "ndbm", ndbm_init, error_reload, ndbm_search, ndbm_mtime, MAPC_INC },
#endif
{ "file", file_init, file_reload, file_search, file_mtime, MAPC_ALL },
{ "error", error_init, error_reload, error_search, error_mtime, MAPC_NONE },
};
static unsigned int
kvhash_of(char *key)
{
unsigned int i, j;
for (i = 0; (j = *key++); i += j)
;
return i % NKVHASH;
}
void
mapc_showtypes(FILE *fp)
{
map_type *mt;
char *sep = "";
for (mt = maptypes; mt < maptypes+sizeof(maptypes)/sizeof(maptypes[0]); mt++) {
fprintf(fp, "%s%s", sep, mt->name);
sep = ", ";
}
}
void
mapc_add_kv(mnt_map *m, char *key, char *val)
{
kv **h;
kv *n;
int hash = kvhash_of(key);
#ifdef DEBUG
dlog("add_kv: %s -> %s", key, val);
#endif
if (MAPC_ISRE(m)) {
char keyb[PATH_MAX];
regex_t *re;
int err;
snprintf(keyb, sizeof(keyb), "^%s$", key);
re = malloc(sizeof(*re));
if (re == NULL) {
plog(XLOG_USER, "error allocating RE \"%s\"", keyb);
return;
}
err = regcomp(re, keyb, 0);
if (err) {
char errbuf[100];
regerror(err, re, errbuf, sizeof errbuf);
free(re);
plog(XLOG_USER, "error compiling RE \"%s\": %s",
keyb, errbuf);
return;
}
free(key);
key = (char *)re;
}
h = &m->kvhash[hash];
n = ALLOC(kv);
n->key = key;
n->val = val;
n->next = *h;
*h = n;
}
static void
mapc_repl_kv(mnt_map *m, char *key, char *val)
{
kv *k;
k = m->kvhash[kvhash_of(key)];
while (k && !FSTREQ(k->key, key))
k = k->next;
if (k) {
free(k->val);
k->val = val;
} else {
mapc_add_kv(m, key, val);
}
}
static int search_map(mnt_map *m, char *key, char **valp)
{
int rc;
do {
rc = (*m->search)(m, m->map_name, key, valp, &m->modify);
if (rc < 0) {
plog(XLOG_MAP, "Re-synchronizing cache for map %s", m->map_name);
mapc_sync(m);
}
} while (rc < 0);
return rc;
}
static void
mapc_find_wildcard(mnt_map *m)
{
int rc = search_map(m, wildcard, &m->wildcard);
if (rc != 0)
m->wildcard = 0;
}
#define mapc_dup(m) ((m)->refc++, (m))
static int
mapc_reload_map(mnt_map *m)
{
int error;
#ifdef DEBUG
dlog("calling map reload on %s", m->map_name);
#endif
error = (*m->reload)(m, m->map_name, mapc_add_kv);
if (error)
return error;
m->wildcard = 0;
#ifdef DEBUG
dlog("calling mapc_search for wildcard");
#endif
error = mapc_search(m, wildcard, &m->wildcard);
if (error)
m->wildcard = 0;
return 0;
}
static mnt_map *
mapc_create(char *map, char *opt)
{
mnt_map *m = ALLOC(mnt_map);
map_type *mt;
time_t modify;
int alloc = 0;
(void) cmdoption(opt, mapc_opt, &alloc);
for (mt = maptypes; mt < maptypes+sizeof(maptypes)/sizeof(maptypes[0]); mt++)
if ((*mt->init)(map, &modify) == 0)
break;
m->flags = alloc & ~MAPC_CACHE_MASK;
alloc &= MAPC_CACHE_MASK;
if (alloc == MAPC_DFLT)
alloc = mt->def_alloc;
switch (alloc) {
default:
plog(XLOG_USER, "Ambiguous map cache type \"%s\"; using \"inc\"", opt);
alloc = MAPC_INC;
case MAPC_NONE:
case MAPC_INC:
case MAPC_ROOT:
break;
case MAPC_ALL:
if (mt->reload == error_reload) {
plog(XLOG_WARNING, "Map type \"%s\" does not support cache type \"all\"; using \"inc\"", mt->name);
alloc = MAPC_INC;
}
break;
case MAPC_RE:
if (mt->reload == error_reload) {
plog(XLOG_WARNING, "Map type \"%s\" does not support cache type \"re\"", mt->name);
mt = &maptypes[sizeof(maptypes)/sizeof(maptypes[0]) - 1];
}
break;
}
#ifdef DEBUG
dlog("Map for %s coming from maptype %s", map, mt->name);
#endif
m->alloc = alloc;
m->reload = mt->reload;
m->modify = modify;
m->search = alloc >= MAPC_ALL ? error_search : mt->search;
m->mtime = mt->mtime;
bzero(m->kvhash, sizeof(m->kvhash));
m->map_name = strdup(map);
m->refc = 1;
m->wildcard = 0;
mapc_sync(m);
return m;
}
static void
mapc_clear(mnt_map *m)
{
int i;
for (i = 0; i < NKVHASH; i++) {
kv *k = m->kvhash[i];
while (k) {
kv *n = k->next;
free(k->key);
free(k->val);
free(k);
k = n;
}
}
bzero(m->kvhash, sizeof(m->kvhash));
if (m->wildcard) {
free(m->wildcard);
m->wildcard = 0;
}
}
mnt_map *
mapc_find(char *map, char *opt)
{
mnt_map *m;
ITER(m, mnt_map, &map_list_head)
if (STREQ(m->map_name, map))
return mapc_dup(m);
m = mapc_create(map, opt);
ins_que(&m->hdr, &map_list_head);
return m;
}
void
mapc_free(void *arg)
{
mnt_map *m = arg;
if (m && --m->refc == 0) {
mapc_clear(m);
free(m->map_name);
rem_que(&m->hdr);
free(m);
}
}
int
mapc_meta_search(mnt_map *m, char *key, char **pval, int recurse)
{
int error = 0;
kv *k = 0;
if (!m) {
plog(XLOG_ERROR, "Null map request for %s", key);
return ENOENT;
}
if (m->flags & MAPC_SYNC) {
time_t t;
error = (*m->mtime)(m->map_name, &t);
if (error || t > m->modify) {
m->modify = t;
plog(XLOG_INFO, "Map %s is out of date", m->map_name);
mapc_sync(m);
}
}
if (!MAPC_ISRE(m)) {
k = m->kvhash[kvhash_of(key)];
while (k && !FSTREQ(k->key, key)) k = k->next;
}
else if (recurse == MREC_FULL) {
int i;
for (i = 0; i < NKVHASH; i++) {
k = m->kvhash[i];
while (k) {
if (regexec((regex_t *)k->key, key,
0, NULL, 0) == 0)
break;
k = k->next;
}
if (k)
break;
}
}
if (k) {
if (k->val)
*pval = strdup(k->val);
else
error = ENOENT;
} else if (m->alloc >= MAPC_ALL) {
error = ENOENT;
} else {
error = search_map(m, key, pval);
if (!error && m->alloc == MAPC_INC)
mapc_add_kv(m, strdup(key), strdup(*pval));
}
if (error > 0) {
if (recurse == MREC_FULL && !MAPC_ISRE(m)) {
char wildname[PATH_MAX];
char *subp;
if (*key == '/')
return error;
strlcpy(wildname, key, sizeof wildname);
while (error && (subp = strrchr(wildname, '/'))) {
strlcpy(subp, "/*", 3);
#ifdef DEBUG
dlog("mapc recurses on %s", wildname);
#endif
error = mapc_meta_search(m, wildname, pval, MREC_PART);
if (error)
*subp = 0;
}
if (error > 0 && m->wildcard) {
*pval = strdup(m->wildcard);
error = 0;
}
}
}
return error;
}
int
mapc_search(mnt_map *m, char *key, char **pval)
{
return mapc_meta_search(m, key, pval, MREC_FULL);
}
static void
mapc_sync(mnt_map *m)
{
if (m->alloc != MAPC_ROOT) {
mapc_clear(m);
if (m->alloc >= MAPC_ALL)
if (mapc_reload_map(m))
m->alloc = MAPC_INC;
if (m->alloc < MAPC_ALL)
mapc_find_wildcard(m);
}
}
void mapc_reload(void)
{
mnt_map *m;
ITER(m, mnt_map, &map_list_head)
mapc_sync(m);
}
static int
root_init(char *map, time_t *tp)
{
*tp = clocktime();
return strcmp(map, ROOT_MAP) == 0 ? 0 : ENOENT;
}
void
root_newmap(char *dir, char *opts, char *map)
{
char str[PATH_MAX];
if (!root_map)
root_map = mapc_find(ROOT_MAP, "mapdefault");
dir = strdup(dir);
if (map)
snprintf(str, sizeof(str), "cache:=mapdefault;type:=toplvl;fs:=\"%s\";%s",
map, opts ? opts : "");
else
strlcpy(str, opts, sizeof str);
mapc_repl_kv(root_map, dir, strdup(str));
}
int
mapc_keyiter(mnt_map *m, void (*fn)(char *,void *), void *arg)
{
int i;
int c = 0;
for (i = 0; i < NKVHASH; i++) {
kv *k = m->kvhash[i];
while (k) {
(*fn)(k->key, arg);
k = k->next;
c++;
}
}
return c;
}
int
root_keyiter(void (*fn)(char *,void *), void *arg)
{
if (root_map) {
int c = mapc_keyiter(root_map, fn, arg);
#ifdef notdef
mapc_free(root_map);
root_map = 0;
#endif
return c;
}
return 0;
}
static int
error_init(char *map, time_t *tp)
{
plog(XLOG_USER, "No source data for map %s", map);
*tp = 0;
return 0;
}
static int
error_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp)
{
return ENOENT;
}
static int
error_reload(mnt_map *m, char *map, add_fn *fn)
{
return ENOENT;
}
static int
error_mtime(char *map, time_t *tp)
{
*tp = 0;
return 0;
}