#include #include #include #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); }