#include <u.h>
#include <libc.h>
#include <ip.h>
#include "nbcache.h"
static Node *Hosts;
static Node *Domains;
static QLock Dblk;
/* these attributes may appear multiple times for a given host */
static int
singleton(char *name)
{
int i;
static char *multivalue[] = { "ip", "ether", "member", "txt", "srv", "failed" };
for(i = 0; i < nelem(multivalue); i++)
if(strcmp(name, multivalue[i]) == 0)
return 0;
return 1;
}
static int
cmpattr(char *s1, char *s2)
{
int i, j;
static char *order[] = {
"dc", "bdc", "dmb", "lmb", "bmb",
"ether", "ip", "dom", "domain", "srv", "hinfo", "txt"
};
for(i = 0; i < nelem(order); i++)
if(strcmp(order[i], s1) == 0)
break;
for(j = 0; j < nelem(order); j++)
if(strcmp(order[j], s2) == 0)
break;
if(i == j)
return strcmp(s1, s2);
return i - j;
}
static void
reapattrs(Attr **ap, int force)
{
Attr *a;
long ttl, now;
now = time(nil);
while(*ap){
a = *ap;
ttl = a->ttl;
if(ttl == -1)
ttl = Defttl;
if(now > a->mtime+ttl || force){
if(Debug == 1)
print("reap attr %s=%s ttl=%ld age=%ld hits=%ld\n",
a->name, a->value, a->ttl, now - a->mtime, a->hits);
if(a->child)
reapattrs(&a->child, 1);
*ap = a->next;
free(a->name);
free(a->value);
free(a);
}
else
ap = &a->next;
}
}
static void
reapnodes(Node **np)
{
Node *n;
long ttl, now;
now = time(nil);
while(*np){
n = *np;
ttl = n->ttl;
if(ttl == -1)
ttl = Defttl;
if(now > n->mtime+ttl){
if(Debug == 1)
print("reap node %s ttl=%ld age=%ld hits=%ld\n",
n->value, n->ttl, now - n->mtime, n->hits);
if(n->attrs)
reapattrs(&n->attrs, 1);
*np = n->next;
free(n->value);
free(n);
}
else{
if(n->attrs)
reapattrs(&n->attrs, 0);
np = &n->next;
}
}
}
/*
* NB: the value param is smprint()'ed in setval() below
*/
static Attr *
doset(Attr **ap, long ttl, char *name, char *value)
{
Attr *a, *new;
int n, unique;
unique = singleton(name);
for(a = *ap; a; ap = &a->next, a = *ap){
n = cmpattr(a->name, name);
if(n == 0 && (unique || strcmp(value, a->value) == 0)){
if(Debug && n == 0 && strcmp(value, a->value) != 0)
print("%s=%s -> %s\n", name, a->value, value);
free(a->value);
a->mtime = time(nil);
a->hits++;
a->value = value;
if(ttl > a->ttl){
if(Debug == 1)
print("doset: %s=%s ttl=%ld->%ld\n", name, value, a->ttl, ttl);
a->ttl = ttl;
}
if(Debug == 2)
print("doset: %s=%s refresh %ld/%ld\n", name, value,
a->ttl - (time(nil) - a->mtime), a->ttl);
return a;
}
if(n > 0)
break;
}
if((new = mallocz(sizeof(Attr), 1)) == nil)
sysfatal("No memory %r\n");
if((new->name = strdup(name)) == nil)
sysfatal("No memory %r\n");
new->ttl = ttl;
new->mtime = time(nil);
new->hits++;
new->value = value;
new->next = a;
*ap = new;
return new;
}
static void
dumptab(Fmt *f, char *name, Node *tab, int first_special)
{
Node *n;
Attr *a, *c;
long ttl, now;
now = time(nil);
for(n = tab; n; n = n->next){
ttl = n->ttl;
if(ttl == -1)
ttl = Defttl;
if(now > n->mtime+ttl)
continue;
fmtprint(f, "%s=%q", name, n->value);
if(! first_special)
fmtprint(f, "\n");
for(a = n->attrs; a; a = a->next){
if(*a->value)
fmtprint(f, "\t%s=%q", a->name, a->value);
else
fmtprint(f, "\t%s=", a->name);
for(c = a->child; c; c = c->next){
if(*c->value)
fmtprint(f, " %s=%q", c->name, c->value);
else
fmtprint(f, " %s=", c->name);
}
fmtprint(f, "\n");
}
fmtprint(f, "\n");
}
}
int
lookval(Attr *a, char *name)
{
int rc;
long now;
rc = 0;
now = time(nil);
qlock(&Dblk);
for(; a; a = a->next)
if(cmpattr(a->name, name) == 0){
a->mtime = now;
a->hits++;
for(a = a->child; a; a = a->next){
a->mtime = now;
a->hits++;
}
rc = 1;
break;
}
qunlock(&Dblk);
return rc;
}
void
addval(Attr *a, char *name, char *fmt, ...)
{
va_list arg;
char *value;
value = nil;
if(fmt){
va_start(arg, fmt);
if((value = vsmprint(fmt, arg)) == nil)
sysfatal("No memory %r\n");
setmalloctag(value, getcallerpc(&a));
va_end(arg);
}
qlock(&Dblk);
doset(&a->child, a->ttl, name, value);
qunlock(&Dblk);
}
Attr *
setval(Node *n, char *name, char *fmt, ...)
{
Attr *a;
va_list arg;
char *value;
va_start(arg, fmt);
if((value = vsmprint(fmt, arg)) == nil)
sysfatal("No memory %r\n");
setmalloctag(value, getcallerpc(&n));
va_end(arg);
qlock(&Dblk);
if(n->attrs)
reapattrs(&n->attrs, 0);
a = doset(&n->attrs, n->ttl, name, value);
qunlock(&Dblk);
return a;
}
Node *
getnode(int type, long ttl, char *fmt, ...)
{
long now;
va_list arg;
Node *n, **rootp;
char *name, *value;
now = time(nil);
va_start(arg, fmt);
if((value = vsmprint(fmt, arg)) == nil)
sysfatal("No memory %r\n");
setmalloctag(value, getcallerpc(&type));
va_end(arg);
qlock(&Dblk);
switch(type){
case Thost:
name="sys";
rootp = &Hosts;
break;
case Tdomain:
name="domain";
rootp = &Domains;
break;
default:
SET(name);
SET(rootp);
sysfatal("%d - unknown object ID\n", type);
}
reapnodes(rootp);
for(n = *rootp; n; n = n->next)
if(strcmp(n->value, value) == 0){
if(Debug == 2)
print("getnode: %s=%s refresh %ld/%ld hits=%ld %p\n", name, value,
n->ttl - (now - n->mtime), n->ttl, n->hits, n->attrs);
n->mtime = now;
n->hits++;
if(ttl > n->ttl){
if(Debug == 2)
print("getnode: %s ttl=%ld->%ld hits=%ld\n",
value, n->ttl, ttl, n->hits);
n->ttl = ttl;
}
break;
}
qunlock(&Dblk);
if(n){
free(value);
return n;
}
if((n = mallocz(sizeof(Node), 1)) == nil)
sysfatal("No memory %r\n");
n->mtime = time(nil);
n->hits++;
n->ttl = ttl;
n->value = value;
if(Debug == 1)
print("getnode: %s=%s new-node ttl=%ld\n", name, value, n->ttl);
qlock(&Dblk);
n->next = *rootp;
*rootp = n;
qunlock(&Dblk);
return n;
}
char *
snapshot(void)
{
Fmt fmt;
fmtstrinit(&fmt);
qlock(&Dblk);
dumptab(&fmt, "domain", Domains, 0);
dumptab(&fmt, "sys", Hosts, 1);
qunlock(&Dblk);
return fmtstrflush(&fmt);
}
|