Add ndblib and iplib
authorRonald G. Minnich <rminnich@google.com>
Tue, 11 Mar 2014 19:04:43 +0000 (12:04 -0700)
committerRonald G. Minnich <rminnich@google.com>
Tue, 11 Mar 2014 19:05:43 +0000 (12:05 -0700)
Needed for connection services

Signed-off-by: Ronald G. Minnich <rminnich@google.com>
56 files changed:
Makefile
user/iplib/FIX [new file with mode: 0644]
user/iplib/Makefile [new file with mode: 0644]
user/iplib/announce.c [new file with mode: 0755]
user/iplib/bo.c [new file with mode: 0755]
user/iplib/classmask.c [new file with mode: 0755]
user/iplib/dial.c [new file with mode: 0755]
user/iplib/eipfmt.c.NOT [new file with mode: 0755]
user/iplib/equivip.c [new file with mode: 0755]
user/iplib/fixincludes [new file with mode: 0644]
user/iplib/getfields.c [new file with mode: 0755]
user/iplib/include/icmp.h [new file with mode: 0755]
user/iplib/include/iplib.h [new file with mode: 0755]
user/iplib/ipaux.c [new file with mode: 0755]
user/iplib/myetheraddr.c [new file with mode: 0755]
user/iplib/myipaddr.c [new file with mode: 0755]
user/iplib/netmkaddr.c [new file with mode: 0755]
user/iplib/parseether.c [new file with mode: 0755]
user/iplib/parseip.c [new file with mode: 0755]
user/iplib/plan9.cocci [new file with mode: 0644]
user/iplib/ptclbsum.c [new file with mode: 0755]
user/iplib/readipifc.c [new file with mode: 0755]
user/iplib/scalar.cocci [new file with mode: 0644]
user/iplib/tokenize.c [new file with mode: 0755]
user/iplib/typedef.cocci [new file with mode: 0644]
user/ndblib/Makefile [new file with mode: 0644]
user/ndblib/convD2M.c [new file with mode: 0644]
user/ndblib/convM2D.c [new file with mode: 0644]
user/ndblib/convM2S.c [new file with mode: 0755]
user/ndblib/convS2M.c [new file with mode: 0755]
user/ndblib/csgetval.c [new file with mode: 0755]
user/ndblib/csipinfo.c [new file with mode: 0755]
user/ndblib/dnsquery.c [new file with mode: 0755]
user/ndblib/fcallfmt.c [new file with mode: 0755]
user/ndblib/include/dir.h [new file with mode: 0755]
user/ndblib/include/fcall.h [new file with mode: 0755]
user/ndblib/include/ndb.h [new file with mode: 0755]
user/ndblib/ipattr.c [new file with mode: 0755]
user/ndblib/ndbaux.c [new file with mode: 0755]
user/ndblib/ndbcache.c [new file with mode: 0755]
user/ndblib/ndbcat.c [new file with mode: 0755]
user/ndblib/ndbconcatenate.c [new file with mode: 0755]
user/ndblib/ndbdiscard.c [new file with mode: 0755]
user/ndblib/ndbfree.c [new file with mode: 0755]
user/ndblib/ndbgetipaddr.c [new file with mode: 0755]
user/ndblib/ndbgetval.c [new file with mode: 0755]
user/ndblib/ndbhash.c [new file with mode: 0755]
user/ndblib/ndbhf.h [new file with mode: 0755]
user/ndblib/ndbipinfo.c [new file with mode: 0755]
user/ndblib/ndblookval.c [new file with mode: 0755]
user/ndblib/ndbopen.c [new file with mode: 0755]
user/ndblib/ndbparse.c [new file with mode: 0755]
user/ndblib/ndbreorder.c [new file with mode: 0755]
user/ndblib/ndbsubstitute.c [new file with mode: 0755]
user/ndblib/read9pmsg.c [new file with mode: 0644]
user/ndblib/setnetmtpt.c [new file with mode: 0755]

index 65a2c3f..ea70544 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -536,8 +536,11 @@ endif #ifeq ($(mixed-targets),1)
 # List all userspace directories here, and state any dependencies between them,
 # such as how pthread depends on parlib.
 
-user-dirs = parlib pthread benchutil
+user-dirs = parlib pthread benchutil iplib ndblib 
 pthread: parlib
+pthread: parlib
+iplib: parlib
+ndblib: iplib
 
 PHONY += install-libs $(user-dirs)
 install-libs: $(user-dirs) symlinks cc-exists
diff --git a/user/iplib/FIX b/user/iplib/FIX
new file mode 100644 (file)
index 0000000..943155e
--- /dev/null
@@ -0,0 +1,4 @@
+spatch --in-place --sp-file scalar.cocci $1
+spatch --in-place --sp-file typedef.cocci $1
+spatch --in-place --sp-file plan9.cocci $1
+sh fixincludes $1
diff --git a/user/iplib/Makefile b/user/iplib/Makefile
new file mode 100644 (file)
index 0000000..aa967e2
--- /dev/null
@@ -0,0 +1,50 @@
+ARCH ?= none   # catch bugs
+CFLAGS_USER += -static -fomit-frame-pointer -g
+LIBNAME = iplib
+
+SRCDIR := 
+OBJDIR := $(SRCDIR)obj
+INCDIR = $(SRCDIR)include
+
+INCS = -I. -I$(INCDIR)
+FINALLIB = $(OBJDIR)/lib$(LIBNAME).a
+
+uc = $(shell echo $(1) | tr a-z A-Z)
+
+LIBUCNAME := $(call uc, $(LIBNAME))
+HEADERS := $(shell find $(INCDIR) -name *.h)
+CFILES  := $(wildcard $(SRCDIR)*.c)
+CFILES  += $(wildcard $(SRCDIR)$(ARCH)/*.c)
+SFILES  := $(wildcard $(SRCDIR)$(ARCH)/*.S)
+OBJS    := $(patsubst %.c, $(OBJDIR)/%.o, $(CFILES)) \
+           $(patsubst %.S, $(OBJDIR)/%.o, $(SFILES))
+
+all: $(FINALLIB)
+       @:
+
+$(OBJDIR)/$(ARCH)/%.o: $(SRCDIR)$(ARCH)/%.S $(HEADERS)
+       @echo + as [$(LIBUCNAME)] $<
+       @mkdir -p $(@D)
+       $(Q)$(CC) $(CFLAGS_USER) $(INCS) -o $@ -c $<
+
+$(OBJDIR)/%.o: $(SRCDIR)%.c $(HEADERS)
+       @echo + cc [$(LIBUCNAME)] $<
+       @mkdir -p $(@D)
+       $(Q)$(CC) $(CFLAGS_USER) $(INCS) -o $@ -c $<
+
+$(FINALLIB): $(OBJS)
+       @echo + ar [$(LIBUCNAME)] $@
+       @mkdir -p $(@D)
+       $(Q)$(AR) rc $@ $(OBJS)
+
+install: $(FINALLIB)
+       @cp $(FINALLIB) $(XCC_TARGET_ROOT)/lib/
+       @cp -R $(INCDIR)/* $(XCC_TARGET_ROOT)/sys-include/
+       @rm -rf $(XCC_TARGET_ROOT)/sys-include/iplib  
+       @ln -fs . $(XCC_TARGET_ROOT)/sys-include/iplib  
+
+clean: 
+       @echo + clean [$(LIBUCNAME)]
+       $(Q)rm -rf $(FINALLIB)
+       $(Q)rm -rf $(OBJDIR)
+
diff --git a/user/iplib/announce.c b/user/iplib/announce.c
new file mode 100755 (executable)
index 0000000..8b3d3dc
--- /dev/null
@@ -0,0 +1,285 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <iplib.h>
+
+static int     nettrans(char*, char*, int na, char*, int);
+
+enum
+{
+       Maxpath=        256,
+};
+
+/*
+ *  announce a network service.
+ */
+int
+announce(char *addr, char *dir)
+{
+       int ctl, n, m;
+       char buf[Maxpath];
+       char buf2[Maxpath];
+       char netdir[Maxpath];
+       char naddr[Maxpath];
+       char *cp;
+
+       /*
+        *  translate the address
+        */
+       if(nettrans(addr, naddr, sizeof(naddr), netdir, sizeof(netdir)) < 0)
+               return -1;
+
+       /*
+        * get a control channel
+        */
+       ctl = open(netdir, O_RDWR);
+       if(ctl<0){
+               fprintf(stderr,"announce opening %s: %r", netdir);
+               return -1;
+       }
+       cp = strrchr(netdir, '/');
+       if(cp == NULL){
+               fprintf(stderr,"announce arg format %s", netdir);
+               close(ctl);
+               return -1;
+       }
+       *cp = 0;
+
+       /*
+        *  find out which line we have
+        */
+       n = snprintf(buf, sizeof(buf), "%s/", netdir);
+       m = read(ctl, &buf[n], sizeof(buf)-n-1);
+       if(m <= 0){
+               fprintf(stderr,"announce reading %s: %r", netdir);
+               close(ctl);
+               return -1;
+       }
+       buf[n+m] = 0;
+
+       /*
+        *  make the call
+        */
+       n = snprintf(buf2, sizeof(buf2), "announce %s", naddr);
+       if(write(ctl, buf2, n)!=n){
+               fprintf(stderr,"announce writing %s: %r", netdir);
+               close(ctl);
+               return -1;
+       }
+
+       /*
+        *  return directory etc.
+        */
+       if(dir){
+               strncpy(dir, buf, NETPATHLEN);
+               dir[NETPATHLEN-1] = 0;
+       }
+       return ctl;
+}
+
+/*
+ *  listen for an incoming call
+ */
+int
+listen(char *dir, char *newdir)
+{
+       int ctl, n, m;
+       char buf[Maxpath];
+       char *cp;
+
+       /*
+        *  open listen, wait for a call
+        */
+       snprintf(buf, sizeof(buf), "%s/listen", dir);
+       ctl = open(buf, O_RDWR);
+       if(ctl < 0){
+               fprintf(stderr,"listen opening %s: %r", buf);
+               return -1;
+       }
+
+       /*
+        *  find out which line we have
+        */
+       strncpy(buf, dir, sizeof(buf) - 1);
+       buf[sizeof(buf) - 1] = 0;
+       cp = strrchr(buf, '/');
+       if(cp == NULL){
+               close(ctl);
+               fprintf(stderr,"listen arg format %s", dir);
+               return -1;
+       }
+       *++cp = 0;
+       n = cp-buf;
+       m = read(ctl, cp, sizeof(buf) - n - 1);
+       if(m <= 0){
+               close(ctl);
+               fprintf(stderr,"listen reading %s/listen: %r", dir);
+               return -1;
+       }
+       buf[n+m] = 0;
+
+       /*
+        *  return directory etc.
+        */
+       if(newdir){
+               strncpy(newdir, buf, NETPATHLEN);
+               newdir[NETPATHLEN-1] = 0;
+       }
+       return ctl;
+
+}
+
+/*
+ *  accept a call, return an fd to the open data file
+ */
+int
+accept(int ctl, char *dir)
+{
+       char buf[Maxpath];
+       char *num;
+       long n;
+
+       num = strrchr(dir, '/');
+       if(num == NULL)
+               num = dir;
+       else
+               num++;
+
+       n = snprintf(buf, sizeof(buf), "accept %s", num);
+       write(ctl, buf, n); /* ignore return value, network might not need accepts */
+
+       snprintf(buf, sizeof(buf), "%s/data", dir);
+       return open(buf, O_RDWR);
+}
+
+/*
+ *  reject a call, tell device the reason for the rejection
+ */
+int
+reject(int ctl, char *dir, char *cause)
+{
+       char buf[Maxpath];
+       char *num;
+       long n;
+
+       num = strrchr(dir, '/');
+       if(num == 0)
+               num = dir;
+       else
+               num++;
+       snprintf(buf, sizeof(buf), "reject %s %s", num, cause);
+       n = strlen(buf);
+       if(write(ctl, buf, n) != n)
+               return -1;
+       return 0;
+}
+
+/*
+ *  perform the identity translation (in case we can't reach cs)
+ */
+static int
+identtrans(char *netdir, char *addr, char *naddr, int na, char *file, int nf)
+{
+       char proto[Maxpath];
+       char *p;
+
+       /* parse the protocol */
+       strncpy(proto, addr, sizeof(proto));
+       proto[sizeof(proto)-1] = 0;
+       p = strchr(proto, '!');
+       if(p)
+               *p++ = 0;
+
+       snprintf(file, nf, "%s/%s/clone", netdir, proto);
+       strncpy(naddr, p, na);
+       naddr[na-1] = 0;
+
+       return 1;
+}
+
+/*
+ *  call up the connection server and get a translation
+ */
+static int
+nettrans(char *addr, char *naddr, int na, char *file, int nf)
+{
+       int i, fd;
+       char buf[Maxpath];
+       char netdir[Maxpath];
+       char *p, *p2;
+       long n;
+
+       /*
+        *  parse, get network directory
+        */
+       p = strchr(addr, '!');
+       if(p == 0){
+               fprintf(stderr,"bad dial string: %s", addr);
+               return -1;
+       }
+       if(*addr != '/'){
+               strncpy(netdir, "/net", sizeof(netdir));
+               netdir[sizeof(netdir) - 1] = 0;
+       } else {
+               for(p2 = p; *p2 != '/'; p2--)
+                       ;
+               i = p2 - addr;
+               if(i == 0 || i >= sizeof(netdir)){
+                       fprintf(stderr,"bad dial string: %s", addr);
+                       return -1;
+               }
+               strncpy(netdir, addr, i);
+               netdir[i] = 0;
+               addr = p2 + 1;
+       }
+
+       /*
+        *  ask the connection server
+        */
+       snprintf(buf, sizeof(buf), "%s/cs", netdir);
+       fd = open(buf, O_RDWR);
+       if(fd < 0)
+               return identtrans(netdir, addr, naddr, na, file, nf);
+       if(write(fd, addr, strlen(addr)) < 0){
+               close(fd);
+               return -1;
+       }
+       lseek(fd, 0, 0);
+       n = read(fd, buf, sizeof(buf)-1);
+       close(fd);
+       if(n <= 0)
+               return -1;
+       buf[n] = 0;
+
+       /*
+        *  parse the reply
+        */
+       p = strchr(buf, ' ');
+       if(p == 0)
+               return -1;
+       *p++ = 0;
+       strncpy(naddr, p, na);
+       naddr[na-1] = 0;
+
+       if(buf[0] == '/'){
+               p = strchr(buf+1, '/');
+               if(p == NULL)
+                       p = buf;
+               else 
+                       p++;
+       }
+       snprintf(file, nf, "%s/%s", netdir, p);
+       return 0;
+}
diff --git a/user/iplib/bo.c b/user/iplib/bo.c
new file mode 100755 (executable)
index 0000000..a60679f
--- /dev/null
@@ -0,0 +1,89 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <iplib.h>
+
+void
+hnputv(void *p, uint64_t v)
+{
+       uint8_t *a;
+
+       a = p;
+       a[0] = v>>56;
+       a[1] = v>>48;
+       a[2] = v>>40;
+       a[3] = v>>32;
+       a[4] = v>>24;
+       a[5] = v>>16;
+       a[6] = v>>8;
+       a[7] = v;
+}
+
+void
+hnputl(void *p, unsigned int v)
+{
+       uint8_t *a;
+
+       a = p;
+       a[0] = v>>24;
+       a[1] = v>>16;
+       a[2] = v>>8;
+       a[3] = v;
+}
+
+void
+hnputs(void *p, uint16_t v)
+{
+       uint8_t *a;
+
+       a = p;
+       a[0] = v>>8;
+       a[1] = v;
+}
+
+uint64_t
+nhgetv(void *p)
+{
+       uint8_t *a;
+       uint64_t v;
+
+       a = p;
+       v = (uint64_t)a[0]<<56;
+       v |= (uint64_t)a[1]<<48;
+       v |= (uint64_t)a[2]<<40;
+       v |= (uint64_t)a[3]<<32;
+       v |= a[4]<<24;
+       v |= a[5]<<16;
+       v |= a[6]<<8;
+       v |= a[7]<<0;
+       return v;
+}
+
+unsigned int
+nhgetl(void *p)
+{
+       uint8_t *a;
+
+       a = p;
+       return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0);
+}
+
+uint16_t
+nhgets(void *p)
+{
+       uint8_t *a;
+
+       a = p;
+       return (a[0]<<8)|(a[1]<<0);
+}
diff --git a/user/iplib/classmask.c b/user/iplib/classmask.c
new file mode 100755 (executable)
index 0000000..e8a6766
--- /dev/null
@@ -0,0 +1,98 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <iplib.h>
+
+static uint8_t classmask[4][16] = {
+       0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0x00,0x00,0x00,
+       0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0x00,0x00,0x00,
+       0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0x00,0x00,
+       0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0x00,
+};
+
+static uint8_t v6loopback[IPaddrlen] = {
+       0, 0, 0, 0,
+       0, 0, 0, 0,
+       0, 0, 0, 0,
+       0, 0, 0, 0x01
+};
+
+static uint8_t v6linklocal[IPaddrlen] = {
+       0xfe, 0x80, 0, 0,
+       0, 0, 0, 0,
+       0, 0, 0, 0,
+       0, 0, 0, 0
+};
+static uint8_t v6linklocalmask[IPaddrlen] = {
+       0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff,
+       0, 0, 0, 0,
+       0, 0, 0, 0
+};
+static int v6llpreflen = 8;    /* link-local prefix length in bytes */
+
+static uint8_t v6multicast[IPaddrlen] = {
+       0xff, 0, 0, 0,
+       0, 0, 0, 0,
+       0, 0, 0, 0,
+       0, 0, 0, 0
+};
+static uint8_t v6multicastmask[IPaddrlen] = {
+       0xff, 0, 0, 0,
+       0, 0, 0, 0,
+       0, 0, 0, 0,
+       0, 0, 0, 0
+};
+static int v6mcpreflen = 1;    /* multicast prefix length */
+
+static uint8_t v6solicitednode[IPaddrlen] = {
+       0xff, 0x02, 0, 0,
+       0, 0, 0, 0,
+       0, 0, 0, 0x01,
+       0xff, 0, 0, 0
+};
+static uint8_t v6solicitednodemask[IPaddrlen] = {
+       0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff,
+       0xff, 0x0, 0x0, 0x0
+};
+static int v6snpreflen = 13;
+
+uint8_t*
+defmask(uint8_t *ip)
+{
+       if(isv4(ip))
+               return classmask[ip[IPv4off]>>6];
+       else {
+               if(ipcmp(ip, v6loopback) == 0)
+                       return IPallbits;
+               else if(memcmp(ip, v6linklocal, v6llpreflen) == 0)
+                       return v6linklocalmask;
+               else if(memcmp(ip, v6solicitednode, v6snpreflen) == 0)
+                       return v6solicitednodemask;
+               else if(memcmp(ip, v6multicast, v6mcpreflen) == 0)
+                       return v6multicastmask;
+               return IPallbits;
+       }
+}
+
+void
+maskip(uint8_t *from, uint8_t *mask, uint8_t *to)
+{
+       int i;
+
+       for(i = 0; i < IPaddrlen; i++)
+               to[i] = from[i] & mask[i];
+}
diff --git a/user/iplib/dial.c b/user/iplib/dial.c
new file mode 100755 (executable)
index 0000000..12b33f9
--- /dev/null
@@ -0,0 +1,136 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#define NAMELEN 28
+#define NETPATHLEN 40
+
+static int
+call(char *clone, char *dest, int *cfdp, char *dir, char *local)
+{
+       int fd, cfd;
+       int n;
+       char name[3*NAMELEN+5];
+       char data[3*NAMELEN+10];
+       char *p;
+
+       cfd = open(clone, O_RDWR);
+       if(cfd < 0)
+               return -1;
+
+       /* get directory name */
+       n = read(cfd, name, sizeof(name)-1);
+       if(n < 0){
+               close(cfd);
+               return -1;
+       }
+       name[n] = 0;
+       p = strrchr(clone, '/');
+       *p = 0;
+       if(dir)
+               sprintf(dir, "%.*s/%.*s", 2*NAMELEN+1, clone, NAMELEN, name);
+       sprintf(data, "%.*s/%.*s/data", 2*NAMELEN+1, clone, NAMELEN, name);
+
+       /* set local side (port number, for example) if we need to */
+       if(local)
+               sprintf(name, "connect %.*s %.*s", 2*NAMELEN, dest, NAMELEN, local);
+       else
+               sprintf(name, "connect %.*s", 2*NAMELEN, dest);
+       /* connect */
+       if(write(cfd, name, strlen(name)) < 0){
+               close(cfd);
+               return -1;
+       }
+
+       /* open data connection */
+       fd = open(data, O_RDWR);
+       if(fd < 0){
+               close(cfd);
+               return -1;
+       }
+       if(cfdp)
+               *cfdp = cfd;
+       else
+               close(cfd);
+       return fd;
+}
+
+int
+dial(char *dest, char *local, char *dir, int *cfdp)
+{
+       char net[128];
+       char netdir[128], csname[NETPATHLEN], *slp;
+       char clone[NAMELEN+12];
+       char *p;
+       int n;
+       int fd;
+       int rv;
+
+       /* go for a standard form net!... */
+       p = strchr(dest, '!');
+       if(p == 0){
+               sprintf(net, "net!%.*s", sizeof(net)-5, dest);
+       } else {
+               strncpy(net, dest, sizeof(net)-1);
+               net[sizeof(net)-1] = 0;
+       }
+
+       slp = strrchr(net, '/');
+       if (slp != 0) {
+               *slp++ = '\0';
+               strcpy(netdir, net);
+               memmove(net, slp, strlen(slp)+1);
+       } else
+               strcpy(netdir, "/net");
+
+       /* call the connection server */
+       sprintf(csname, "%s/cs", netdir);
+       fd = open(csname, O_RDWR);
+       if(fd < 0){
+               /* no connection server, don't translate */
+               p = strchr(net, '!');
+               *p++ = 0;
+               sprintf(clone, "%s/%s/clone", netdir, net);
+               return call(clone, p, cfdp, dir, local);
+       }
+
+       /*
+        *  send dest to connection to translate
+        */
+       if(write(fd, net, strlen(net)) < 0){
+               close(fd);
+               return -1;
+       }
+
+       /*
+        *  loop through each address from the connection server till
+        *  we get one that works.
+        */
+       rv = -1;
+       lseek(fd, 0, 0);
+       while((n = read(fd, net, sizeof(net) - 1)) > 0){
+               net[n] = 0;
+               p = strchr(net, ' ');
+               if(p == 0)
+                       continue;
+               *p++ = 0;
+               rv = call(net, p, cfdp, dir, local);
+               if(rv >= 0)
+                       break;
+       }
+       close(fd);
+       return rv;
+}
diff --git a/user/iplib/eipfmt.c.NOT b/user/iplib/eipfmt.c.NOT
new file mode 100755 (executable)
index 0000000..ee04c5d
--- /dev/null
@@ -0,0 +1,109 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+
+enum
+{
+       Isprefix= 16,
+};
+
+uchar prefixvals[256] =
+{
+[0x00] 0 | Isprefix,
+[0x80] 1 | Isprefix,
+[0xC0] 2 | Isprefix,
+[0xE0] 3 | Isprefix,
+[0xF0] 4 | Isprefix,
+[0xF8] 5 | Isprefix,
+[0xFC] 6 | Isprefix,
+[0xFE] 7 | Isprefix,
+[0xFF] 8 | Isprefix,
+};
+
+int
+eipfmt(Fmt *f)
+{
+       char buf[5*8];
+       static char *efmt = "%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux";
+       static char *ifmt = "%d.%d.%d.%d";
+       uchar *p, ip[16];
+       ulong *lp;
+       ushort s;
+       int i, j, n, eln, eli;
+
+       switch(f->r) {
+       case 'E':               /* Ethernet address */
+               p = va_arg(f->args, uchar*);
+               snprint(buf, sizeof buf, efmt, p[0], p[1], p[2], p[3], p[4], p[5]);
+               return fmtstrcpy(f, buf);
+
+       case 'I':               /* Ip address */
+               p = va_arg(f->args, uchar*);
+common:
+               if(memcmp(p, v4prefix, 12) == 0){
+                       snprint(buf, sizeof buf, ifmt, p[12], p[13], p[14], p[15]);
+                       return fmtstrcpy(f, buf);
+               }
+
+               /* find longest elision */
+               eln = eli = -1;
+               for(i = 0; i < 16; i += 2){
+                       for(j = i; j < 16; j += 2)
+                               if(p[j] != 0 || p[j+1] != 0)
+                                       break;
+                       if(j > i && j - i > eln){
+                               eli = i;
+                               eln = j - i;
+                       }
+               }
+
+               /* print with possible elision */
+               n = 0;
+               for(i = 0; i < 16; i += 2){
+                       if(i == eli){
+                               n += sprint(buf+n, "::");
+                               i += eln;
+                               if(i >= 16)
+                                       break;
+                       } else if(i != 0)
+                               n += sprint(buf+n, ":");
+                       s = (p[i]<<8) + p[i+1];
+                       n += sprint(buf+n, "%ux", s);
+               }
+               return fmtstrcpy(f, buf);
+
+       case 'i':               /* v6 address as 4 longs */
+               lp = va_arg(f->args, ulong*);
+               for(i = 0; i < 4; i++)
+                       hnputl(ip+4*i, *lp++);
+               p = ip;
+               goto common;
+
+       case 'V':               /* v4 ip address */
+               p = va_arg(f->args, uchar*);
+               snprint(buf, sizeof buf, ifmt, p[0], p[1], p[2], p[3]);
+               return fmtstrcpy(f, buf);
+
+       case 'M':               /* ip mask */
+               p = va_arg(f->args, uchar*);
+
+               /* look for a prefix mask */
+               for(i = 0; i < 16; i++)
+                       if(p[i] != 0xff)
+                               break;
+               if(i < 16){
+                       if((prefixvals[p[i]] & Isprefix) == 0)
+                               goto common;
+                       for(j = i+1; j < 16; j++)
+                               if(p[j] != 0)
+                                       goto common;
+                       n = 8*i + (prefixvals[p[i]] & ~Isprefix);
+               } else
+                       n = 8*16;
+
+               /* got one, use /xx format */
+               snprint(buf, sizeof buf, "/%d", n);
+               return fmtstrcpy(f, buf);
+       }
+       return fmtstrcpy(f, "(eipfmt)");
+}
diff --git a/user/iplib/equivip.c b/user/iplib/equivip.c
new file mode 100755 (executable)
index 0000000..5b999f4
--- /dev/null
@@ -0,0 +1,37 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <iplib.h>
+
+int
+equivip4(uint8_t *a, uint8_t *b)
+{
+       int i;
+
+       for(i = 0; i < 4; i++)
+               if(a[i] != b[i])
+                       return 0;
+       return 1;
+}
+
+int
+equivip6(uint8_t *a, uint8_t *b)
+{
+       int i;
+
+       for(i = 0; i < IPaddrlen; i++)
+               if(a[i] != b[i])
+                       return 0;
+       return 1;
+}
diff --git a/user/iplib/fixincludes b/user/iplib/fixincludes
new file mode 100644 (file)
index 0000000..117100d
--- /dev/null
@@ -0,0 +1,10 @@
+#!/bin/sh
+# N.B. Preserve blank line we match against so this is idempotent.
+sed -i '/^#include/,/^$/c\
+#include <stdlib.h>\
+#include <stdio.h>\
+#include <parlib.h>\
+#include <unistd.h>\
+#include <signal.h>\
+#include <iplib.h>\
+' $1
diff --git a/user/iplib/getfields.c b/user/iplib/getfields.c
new file mode 100755 (executable)
index 0000000..749fb04
--- /dev/null
@@ -0,0 +1,45 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <string.h>
+
+
+/* currently only used in arp.c; so we don't do the full monty. */
+int
+getfields(char *str, char **args, int max, int mflag, char *set)
+{
+       int nr, intok, narg;
+
+       if(max <= 0)
+               return 0;
+
+       narg = 0;
+       args[narg] = str;
+       if(!mflag)
+               narg++;
+       intok = 0;
+       for(;; str++) {
+               nr = *str;
+               if(nr == 0)
+                       break;
+               if(index(set, nr)){
+                       if(narg >= max)
+                               break;
+                       *str = 0;
+                       intok = 0;
+                       args[narg] = str + 1;
+                       if(!mflag)
+                               narg++;
+               } else {
+                       if(!intok && mflag)
+                               narg++;
+                       intok = 1;
+               }
+       }
+       return narg;
+}
diff --git a/user/iplib/include/icmp.h b/user/iplib/include/icmp.h
new file mode 100755 (executable)
index 0000000..22cd370
--- /dev/null
@@ -0,0 +1,82 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+/* ICMP for IP v4 and v6 */
+
+enum
+{
+       /* Packet Types, icmp v4 (rfc 792) */
+       EchoReply       = 0,
+       Unreachable     = 3,
+       SrcQuench       = 4,
+       Redirect        = 5,
+       EchoRequest     = 8,
+       TimeExceed      = 11,
+       InParmProblem   = 12,
+       Timestamp       = 13,
+       TimestampReply  = 14,
+       InfoRequest     = 15,
+       InfoReply       = 16,
+       AddrMaskRequest = 17,
+       AddrMaskReply   = 18,
+       Traceroute      = 30,
+       IPv6WhereAreYou = 33,
+       IPv6IAmHere     = 34,
+
+       /* packet types, icmp v6 (rfc 2463) */
+
+        /* error messages */
+       UnreachableV6   = 1,
+       PacketTooBigV6  = 2,
+       TimeExceedV6    = 3,
+       ParamProblemV6  = 4,
+
+        /* informational messages (rfc 2461 also) */
+       EchoRequestV6   = 128,
+       EchoReplyV6     = 129,
+       RouterSolicit   = 133,
+       RouterAdvert    = 134,
+       NbrSolicit      = 135,
+       NbrAdvert       = 136,
+       RedirectV6      = 137,
+
+       Maxtype6        = 137,
+
+       ICMP_HDRSIZE    = 8,
+};
+
+struct ip4hdr
+{
+       uint8_t vihl;           /* Version and header length */
+       uint8_t tos;            /* Type of service */
+       uint8_t length[2];      /* packet length */
+       uint8_t id[2];          /* Identification */
+       uint8_t frag[2];        /* Fragment information */
+       uint8_t ttl;            /* Time to live */
+       uint8_t proto;          /* Protocol */
+       uint8_t ipcksum[2];     /* Header checksum */
+       uint8_t src[4];         /* Ipv4 source */
+       uint8_t dst[4];         /* Ipv4 destination */
+
+       uint8_t data[];
+};
+
+// #define IP4HDRSZ offsetof(Ip4hdr, data[0])
+
+/* the icmp payload has the same format in v4 and v6 */
+
+struct icmphdr {
+       uint8_t type;
+       uint8_t code;
+       uint8_t cksum[2];
+       uint8_t icmpid[2];
+       uint8_t seq[2];
+       uint8_t data[];
+};
+
+// #define ICMPHDRSZ offsetof(Icmphdr, data[0])
diff --git a/user/iplib/include/iplib.h b/user/iplib/include/iplib.h
new file mode 100755 (executable)
index 0000000..27017db
--- /dev/null
@@ -0,0 +1,234 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#ifndef ROS_INC_IPLIB_H
+
+#define ROS_INC_IPLIB_H
+
+#include <ros/common.h>
+
+enum 
+{
+       IPaddrlen=      16,
+       IPv4addrlen=    4,
+       IPv4off=        12,
+       IPllen=         4,
+       IPV4HDR_LEN=    20,
+
+       /* vihl & vcf[0] values */
+       IP_VER4=        0x40,
+       IP_VER6=        0x60,
+       NETPATHLEN=     40,
+};
+
+/*
+ *  for reading /net/ipifc
+ */
+
+/* local address */
+struct iplifc
+{
+       struct iplifc   *next;
+
+       /* per address on the ip interface */
+       uint8_t ip[IPaddrlen];
+       uint8_t mask[IPaddrlen];
+       uint8_t net[IPaddrlen];         /* ip & mask */
+       uint32_t        preflt;                 /* preferred lifetime */
+       uint32_t        validlt;                /* valid lifetime */
+};
+
+/* default values, one per stack */
+struct ipv6rp
+{
+       int     mflag;
+       int     oflag;
+       int     maxraint;
+       int     minraint;
+       int     linkmtu;
+       int     reachtime;
+       int     rxmitra;
+       int     ttl;
+       int     routerlt;       
+};
+
+/* actual interface */
+struct ipifc
+{
+       struct ipifc    *next;
+       struct iplifc   *lifc;
+
+       /* per ip interface */
+       int     index;                  /* number of interface in ipifc dir */
+       char    dev[64];
+       uint8_t sendra6;                /* on == send router adv */
+       uint8_t recvra6;                /* on == rcv router adv */
+       int     mtu;
+       uint32_t        pktin;
+       uint32_t        pktout;
+       uint32_t        errin;
+       uint32_t        errout;
+       struct ipv6rp   rp;
+};
+
+#define ISIPV6MCAST(addr)      ((addr)[0] == 0xff)
+#define ISIPV6LINKLOCAL(addr) ((addr)[0] == 0xfe && ((addr)[1] & 0xc0) == 0x80)
+
+/*
+ * ipv6 constants
+ * `ra' is `router advertisement', `rs' is `router solicitation'.
+ * `na' is `neighbour advertisement'.
+ */
+enum {
+       IPV6HDR_LEN     = 40,
+
+       /* neighbour discovery option types */
+       V6nd_srclladdr  = 1,
+       V6nd_targlladdr = 2,
+       V6nd_pfxinfo    = 3,
+       V6nd_redirhdr   = 4,
+       V6nd_mtu        = 5,
+       /* new since rfc2461; see iana.org/assignments/icmpv6-parameters */
+       V6nd_home       = 8,
+       V6nd_srcaddrs   = 9,            /* rfc3122 */
+       V6nd_ip         = 17,
+       /* /lib/rfc/drafts/draft-jeong-dnsop-ipv6-dns-discovery-12.txt */
+       V6nd_rdns       = 25,
+       /* plan 9 extensions */
+       V6nd_9fs        = 250,
+       V6nd_9auth      = 251,
+
+       /* Router constants (all times in ms.) */
+       Maxv6initraintvl= 16000,
+       Maxv6initras    = 3,
+       Maxv6finalras   = 3,
+       Minv6interradelay= 3000,
+       Maxv6radelay    = 500,
+
+       /* Host constants */
+       Maxv6rsdelay    = 1000,
+       V6rsintvl       = 4000,
+       Maxv6rss        = 3,
+
+       /* Node constants */
+       Maxv6mcastrss   = 3,
+       Maxv6unicastrss = 3,
+       Maxv6anycastdelay= 1000,
+       Maxv6na         = 3,
+       V6reachabletime = 30000,
+       V6retranstimer  = 1000,
+       V6initprobedelay= 5000,
+};
+
+# if 0
+/* in icmp.h? */
+struct ip4hdr
+{
+       uint8_t vihl;           /* Version and header length */
+       uint8_t tos;            /* Type of service */
+       uint8_t length[2];      /* packet length */
+       uint8_t id[2];          /* ip->identification */
+       uint8_t frag[2];        /* Fragment information */
+       uint8_t ttl;            /* Time to live */
+       uint8_t proto;          /* Protocol */
+       uint8_t cksum[2];       /* Header checksum */
+       uint8_t src[4];         /* IP source */
+       uint8_t dst[4];         /* IP destination */
+};
+#endif
+
+/* V6 header on the wire */
+
+struct ip6hdr {
+       uint8_t vcf[4];         /* version:4, traffic class:8, flow label:20 */
+       uint8_t ploadlen[2];    /* payload length: packet length - 40 */
+       uint8_t proto;          /* next header type */
+       uint8_t ttl;            /* hop limit */
+       uint8_t src[IPaddrlen]; /* source address */
+       uint8_t dst[IPaddrlen]; /* destination address */
+       uint8_t payload[];
+};
+
+/*
+ *  user-level icmpv6 with control message "headers"
+ */
+
+struct icmp6hdr {
+       uint8_t _0_[8];
+       uint8_t laddr[IPaddrlen];       /* local address */
+       uint8_t raddr[IPaddrlen];       /* remote address */
+};
+
+/*
+ *  user level udp headers with control message "headers"
+ */
+enum 
+{
+       Udphdrsize=     52,     /* size of a Udphdr */
+};
+
+struct udphdr
+{
+       uint8_t raddr[IPaddrlen];       /* V6 remote address */
+       uint8_t laddr[IPaddrlen];       /* V6 local address */
+       uint8_t ifcaddr[IPaddrlen];     /* V6 ifc addr msg was received on */
+       uint8_t rport[2];               /* remote port */
+       uint8_t lport[2];               /* local port */
+};
+
+uint8_t*       defmask(uint8_t*);
+void   maskip(uint8_t*, uint8_t*, uint8_t*);
+//int  eipfmt(Fmt*);
+int    isv4(uint8_t*);
+int64_t        parseip(uint8_t*, char*);
+int64_t        parseipmask(uint8_t*, char*);
+char*  v4parseip(uint8_t*, char*);
+char*  v4parsecidr(uint8_t*, uint8_t*, char*);
+int    parseether(uint8_t*, char*);
+int    myipaddr(uint8_t*, char*);
+int    myetheraddr(uint8_t*, char*);
+int    equivip4(uint8_t*, uint8_t*);
+int    equivip6(uint8_t*, uint8_t*);
+
+struct ipifc*  readipifc(char*, struct ipifc*, int);
+
+void   hnputv(void*, uint64_t);
+void   hnputl(void*, unsigned int);
+void   hnputs(void*, uint16_t);
+uint64_t       nhgetv(void*);
+unsigned int   nhgetl(void*);
+uint16_t       nhgets(void*);
+uint16_t       ptclbsum(uint8_t*, int);
+
+int    v6tov4(uint8_t*, uint8_t*);
+void   v4tov6(uint8_t*, uint8_t*);
+
+#define        ipcmp(x, y) memcmp(x, y, IPaddrlen)
+#define        ipmove(x, y) memmove(x, y, IPaddrlen)
+
+extern uint8_t IPv4bcast[IPaddrlen];
+extern uint8_t IPv4bcastobs[IPaddrlen];
+extern uint8_t IPv4allsys[IPaddrlen];
+extern uint8_t IPv4allrouter[IPaddrlen];
+extern uint8_t IPnoaddr[IPaddrlen];
+extern uint8_t v4prefix[IPaddrlen];
+extern uint8_t IPallbits[IPaddrlen];
+
+#define CLASS(p) ((*(uint8_t*)(p))>>6)
+
+int tokenize(char *s, char **args, int maxargs);
+int getfields(char *str, char **args, int max, int mflag, char *unused_set);
+char *netmkaddr(char *linear, char *defnet, char *defsrv);
+int dial(char *dest, char *local, char *dir, int *cfdp);
+int announce(char *addr, char *dir);
+int listen(char *dir, char *newdir);
+int accept(int ctl, char *dir);
+int reject(int ctl, char *dir, char *cause);
+
+
+#endif /* ROS_INC_IPLIB_H */
diff --git a/user/iplib/ipaux.c b/user/iplib/ipaux.c
new file mode 100755 (executable)
index 0000000..e246b60
--- /dev/null
@@ -0,0 +1,114 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <iplib.h>
+
+/*
+ *  well known IP addresses
+ */
+uint8_t IPv4bcast[IPaddrlen] = {
+       0, 0, 0, 0,
+       0, 0, 0, 0,
+       0, 0, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff
+};
+uint8_t IPv4allsys[IPaddrlen] = {
+       0, 0, 0, 0,
+       0, 0, 0, 0,
+       0, 0, 0xff, 0xff,
+       0xe0, 0, 0, 0x01
+};
+uint8_t IPv4allrouter[IPaddrlen] = {
+       0, 0, 0, 0,
+       0, 0, 0, 0,
+       0, 0, 0xff, 0xff,
+       0xe0, 0, 0, 0x02
+};
+uint8_t IPallbits[IPaddrlen] = {
+       0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff
+};
+uint8_t IPnoaddr[IPaddrlen];
+
+/*
+ *  prefix of all v4 addresses
+ */
+uint8_t v4prefix[IPaddrlen] = {
+       0, 0, 0, 0,
+       0, 0, 0, 0,
+       0, 0, 0xff, 0xff,
+       0, 0, 0, 0
+};
+
+int
+isv4(uint8_t *ip)
+{
+       return memcmp(ip, v4prefix, IPv4off) == 0;
+}
+
+/*
+ *  the following routines are unrolled with no memset's to speed
+ *  up the usual case
+ */
+void
+v4tov6(uint8_t *v6, uint8_t *v4)
+{
+       v6[0] = 0;
+       v6[1] = 0;
+       v6[2] = 0;
+       v6[3] = 0;
+       v6[4] = 0;
+       v6[5] = 0;
+       v6[6] = 0;
+       v6[7] = 0;
+       v6[8] = 0;
+       v6[9] = 0;
+       v6[10] = 0xff;
+       v6[11] = 0xff;
+       v6[12] = v4[0];
+       v6[13] = v4[1];
+       v6[14] = v4[2];
+       v6[15] = v4[3];
+}
+
+int
+v6tov4(uint8_t *v4, uint8_t *v6)
+{
+       if(v6[0] == 0
+       && v6[1] == 0
+       && v6[2] == 0
+       && v6[3] == 0
+       && v6[4] == 0
+       && v6[5] == 0
+       && v6[6] == 0
+       && v6[7] == 0
+       && v6[8] == 0
+       && v6[9] == 0
+       && v6[10] == 0xff
+       && v6[11] == 0xff)
+       {
+               v4[0] = v6[12];
+               v4[1] = v6[13];
+               v4[2] = v6[14];
+               v4[3] = v6[15];
+               return 0;
+       } else {
+               memset(v4, 0, 4);
+               if(memcmp(v6, IPnoaddr, IPaddrlen) == 0)
+                       return 0;
+               return -1;
+       }
+}
diff --git a/user/iplib/myetheraddr.c b/user/iplib/myetheraddr.c
new file mode 100755 (executable)
index 0000000..931ed8d
--- /dev/null
@@ -0,0 +1,43 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <iplib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+int
+myetheraddr(uint8_t *to, char *dev)
+{
+       int n, fd;
+       char buf[256];
+
+       if(*dev == '/')
+               sprintf(buf, "%s/addr", dev);
+       else
+               sprintf(buf, "/net/%s/addr", dev);
+
+       fd = open(buf, O_RDONLY);
+       if(fd < 0)
+               return -1;
+
+       n = read(fd, buf, sizeof buf -1 );
+       close(fd);
+       if(n <= 0)
+               return -1;
+       buf[n] = 0;
+
+       parseether(to, buf);
+       return 0;
+}
diff --git a/user/iplib/myipaddr.c b/user/iplib/myipaddr.c
new file mode 100755 (executable)
index 0000000..63b246a
--- /dev/null
@@ -0,0 +1,54 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <iplib.h>
+
+static uint8_t loopbacknet[IPaddrlen] = {
+       0, 0, 0, 0,
+       0, 0, 0, 0,
+       0, 0, 0xff, 0xff,
+       127, 0, 0, 0
+};
+static uint8_t loopbackmask[IPaddrlen] = {
+       0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff,
+       0xff, 0, 0, 0
+};
+
+// find first ip addr that isn't the friggin loopback address
+// unless there are no others
+int
+myipaddr(uint8_t *ip, char *net)
+{
+       struct ipifc *nifc;
+       struct iplifc *lifc;
+       static struct ipifc *ifc;
+       uint8_t mynet[IPaddrlen];
+
+       ifc = readipifc(net, ifc, -1);
+       for(nifc = ifc; nifc; nifc = nifc->next)
+               for(lifc = nifc->lifc; lifc; lifc = lifc->next){
+                       maskip(lifc->ip, loopbackmask, mynet);
+                       if(ipcmp(mynet, loopbacknet) == 0){
+                               continue;
+                       }
+                       if(ipcmp(lifc->ip, IPnoaddr) != 0){
+                               ipmove(ip, lifc->ip);
+                               return 0;
+                       }
+               }
+       ipmove(ip, IPnoaddr);
+       return -1;
+}
diff --git a/user/iplib/netmkaddr.c b/user/iplib/netmkaddr.c
new file mode 100755 (executable)
index 0000000..5bd768c
--- /dev/null
@@ -0,0 +1,72 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+/* truncate functionality for now. No lib/ndb.
+#include <libnet.h>
+ */
+
+/*
+ *  make an address, add the defaults
+ *
+ *  if you pass in only one ! in linear, this assumes this ! was between the net
+ *  and the host.  if you pass in no !s, we'll build one from defnet/defsrv.
+ */
+char *
+netmkaddr(char *linear, char *defnet, char *defsrv)
+{
+       /* TODO: this isn't threadsafe */
+       static char addr[256];
+       char *cp;
+
+       /*
+        *  dump network name
+        */
+       cp = strchr(linear, '!');
+       if(cp == 0){
+               if(defnet==0){
+                       if(defsrv)
+                               snprintf(addr, sizeof(addr), "net!%s!%s",
+                                       linear, defsrv);
+                       else
+                               snprintf(addr, sizeof(addr), "net!%s", linear);
+               }
+               else {
+                       if(defsrv)
+                               snprintf(addr, sizeof(addr), "%s!%s!%s", defnet,
+                                       linear, defsrv);
+                       else
+                               snprintf(addr, sizeof(addr), "%s!%s", defnet,
+                                       linear);
+               }
+               return addr;
+       }
+
+       /*
+        *  if there is already a service, use it
+        */
+       cp = strchr(cp+1, '!');
+       if(cp)
+               return linear;
+
+       /*
+        *  add default service
+        */
+       if(defsrv == 0)
+               return linear;
+       snprintf(addr, sizeof(addr), "%s!%s", linear, defsrv);
+
+       return addr;
+}
diff --git a/user/iplib/parseether.c b/user/iplib/parseether.c
new file mode 100755 (executable)
index 0000000..f5864af
--- /dev/null
@@ -0,0 +1,38 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <iplib.h>
+
+int
+parseether(uint8_t *to, char *from)
+{
+       char nip[4];
+       char *p;
+       int i;
+
+       p = from;
+       for(i = 0; i < 6; i++){
+               if(*p == 0)
+                       return -1;
+               nip[0] = *p++;
+               if(*p == 0)
+                       return -1;
+               nip[1] = *p++;
+               nip[2] = 0;
+               to[i] = strtoul(nip, 0, 16);
+               if(*p == ':')
+                       p++;
+       }
+       return 0;
+}
diff --git a/user/iplib/parseip.c b/user/iplib/parseip.c
new file mode 100755 (executable)
index 0000000..cb0db85
--- /dev/null
@@ -0,0 +1,200 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <iplib.h>
+
+static int isascii(int c) {return ((c >=0) && (c <= 128));}
+static int isalnum(int c) {return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||(c >= '0' && c <= '9'));}
+static int isdigit(int c) {return ((c >= '0' && c <= '9'));}
+static int isxdigit(int c) {return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ||(c >= '0' && c <= '9'));}
+
+char*
+v4parseip(uint8_t *to, char *from)
+{
+       int i;
+       char *p;
+
+       p = from;
+       for(i = 0; i < 4 && *p; i++){
+               to[i] = strtoul(p, &p, 0);
+               if(*p == '.')
+                       p++;
+       }
+       switch(CLASS(to)){
+       case 0: /* class A - 1 uchar net */
+       case 1:
+               if(i == 3){
+                       to[3] = to[2];
+                       to[2] = to[1];
+                       to[1] = 0;
+               } else if (i == 2){
+                       to[3] = to[1];
+                       to[1] = 0;
+               }
+               break;
+       case 2: /* class B - 2 uchar net */
+               if(i == 3){
+                       to[3] = to[2];
+                       to[2] = 0;
+               }
+               break;
+       }
+       return p;
+}
+
+static int
+ipcharok(int c)
+{
+       return c == '.' || c == ':' || isascii(c) && isxdigit(c);
+}
+
+static int
+delimchar(int c)
+{
+       if(c == '\0')
+               return 1;
+       if(c == '.' || c == ':' || isascii(c) && isalnum(c))
+               return 0;
+       return 1;
+}
+
+/*
+ * `from' may contain an address followed by other characters,
+ * at least in /boot, so we permit whitespace (and more) after the address.
+ * we do ensure that "delete" cannot be parsed as "de::".
+ *
+ * some callers don't check the return value for errors, so
+ * set `to' to something distinctive in the case of a parse error.
+ */
+int64_t
+parseip(uint8_t *to, char *from)
+{
+       int i, elipsis = 0, v4 = 1;
+       uint32_t x;
+       char *p, *op;
+
+       memset(to, 0, IPaddrlen);
+       p = from;
+       for(i = 0; i < IPaddrlen && ipcharok(*p); i+=2){
+               op = p;
+               x = strtoul(p, &p, 16);
+               if(*p == '.' || (*p == 0 && i == 0)){   /* ends with v4? */
+                       p = v4parseip(to+i, op);
+                       i += 4;
+                       break;
+               }
+               /* v6: at most 4 hex digits, followed by colon or delim */
+               if(x != (uint16_t)x || *p != ':' && !delimchar(*p)) {
+                       memset(to, 0, IPaddrlen);
+                       return -1;                      /* parse error */
+               }
+               to[i] = x>>8;
+               to[i+1] = x;
+               if(*p == ':'){
+                       v4 = 0;
+                       if(*++p == ':'){        /* :: is elided zero short(s) */
+                               if (elipsis) {
+                                       memset(to, 0, IPaddrlen);
+                                       return -1;      /* second :: */
+                               }
+                               elipsis = i+2;
+                               p++;
+                       }
+               } else if (p == op)             /* strtoul made no progress? */
+                       break;
+       }
+       if (p == from || !delimchar(*p)) {
+               memset(to, 0, IPaddrlen);
+               return -1;                              /* parse error */
+       }
+       if(i < IPaddrlen){
+               memmove(&to[elipsis+IPaddrlen-i], &to[elipsis], i-elipsis);
+               memset(&to[elipsis], 0, IPaddrlen-i);
+       }
+       if(v4){
+               to[10] = to[11] = 0xff;
+               return nhgetl(to + IPv4off);
+       } else
+               return 6;
+}
+
+/*
+ *  hack to allow ip v4 masks to be entered in the old
+ *  style
+ */
+int64_t
+parseipmask(uint8_t *to, char *from)
+{
+       int i, w;
+       int64_t x;
+       uint8_t *p;
+
+       if(*from == '/'){
+               /* as a number of prefix bits */
+               i = atoi(from+1);
+               if(i < 0)
+                       i = 0;
+               if(i > 128)
+                       i = 128;
+               w = i;
+               memset(to, 0, IPaddrlen);
+               for(p = to; i >= 8; i -= 8)
+                       *p++ = 0xff;
+               if(i > 0)
+                       *p = ~((1<<(8-i))-1);
+               x = nhgetl(to+IPv4off);
+               /*
+                * identify as ipv6 if the mask is inexpressible as a v4 mask
+                * (because it has too few mask bits).  Arguably, we could
+                * always return 6 here.
+                */
+               if (w < 8*(IPaddrlen-IPv4addrlen))
+                       return 6;
+       } else {
+               /* as a straight v4 bit mask */
+               x = parseip(to, from);
+               if (x != -1)
+                       x = (uint32_t)nhgetl(to + IPv4off);
+               if(memcmp(to, v4prefix, IPv4off) == 0)
+                       memset(to, 0xff, IPv4off);
+       }
+       return x;
+}
+
+/*
+ *  parse a v4 ip address/mask in cidr format
+ */
+char*
+v4parsecidr(uint8_t *addr, uint8_t *mask, char *from)
+{
+       int i;
+       char *p;
+       uint8_t *a;
+
+       p = v4parseip(addr, from);
+
+       if(*p == '/'){
+               /* as a number of prefix bits */
+               i = strtoul(p+1, &p, 0);
+               if(i > 32)
+                       i = 32;
+               memset(mask, 0, IPv4addrlen);
+               for(a = mask; i >= 8; i -= 8)
+                       *a++ = 0xff;
+               if(i > 0)
+                       *a = ~((1<<(8-i))-1);
+       } else 
+               memcpy(mask, defmask(addr), IPv4addrlen);
+       return p;
+}
diff --git a/user/iplib/plan9.cocci b/user/iplib/plan9.cocci
new file mode 100644 (file)
index 0000000..a92a151
--- /dev/null
@@ -0,0 +1,158 @@
+@ print@
+@@
+-print(
++printf(
+...)
+
+@ fprint@
+expression E;
+@@
+-fprint(E,
++fprintf(stderr,
+...)
+
+@USED@
+@@
+-USED(...);
+
+@NOTED@
+expression E;
+@@
+-noted(E);
++ignore(E);fprintf(stderr, "noted\n"); exit(1);
+
+@exits@
+expression E;
+@@
+-exits(E);
++fprintf(stderr, E); exit(1);
+
+@ channel @
+identifier d;
+@@
+-Chan
++struct chan
+d;
+
+@ channelstar @
+identifier d;
+@@
+-Chan *
++struct chan *
+d;
+
+@mount@
+identifier d;
+@@
+-Mount
++struct mount
+d;
+
+@ mountstar @
+identifier d;
+@@
+-Chan *
++struct mount *
+d;
+
+@uvlong@
+identifier d;
+@@
+-uvlong
++uint64_t
+d;
+@vlong@
+identifier d;
+@@
+-vlong
++int64_t
+d;
+@ulong@
+identifier d;
+@@
+-ulong
++uint32_t
+d;
+@ushort@
+identifier d;
+@@
+-ushort
++uint16_t
+d;
+
+@ rulesm @
+identifier t;
+identifier f;
+expression E1;
+type T;
+@@
+T f(...){<...
+t = smalloc(E1);
+...>}
+@@
+identifier rulesm.f;
+expression E1;
+@@
+
+- smalloc(E1
++ calloc(E1, 1
+   )
+
+@ rulem @
+identifier t;
+identifier f;
+expression E1;
+type T;
+@@
+T f(...){<...
+t = malloc(E1);
+...>}
+@@
+identifier rulem.f;
+expression E1;
+@@
+
+- malloc(E1
++ calloc(E1, 1
+   )
+
+@@
+@@
+-getcallerpc(...);
+@@
+@@
+-setmalloctag(...);
+
+@@
+type T;
+@@
+-T validname0(...){...}
+
+@@
+type T;
+@@
+-T kstrcpy(...){...}
+
+@@
+@@
+-if (up){
++if (current){
+...  } else {...}
+
+@@
+expression E;
+@@
+-strcpy(up->errstr,
++set_errstr(
+E)
+@@
+@@
+-saveregisters(...);
+@@
+@@
+-saveregisters(...){...}
+@@
+@@
++//
+muxclose(...);
+
diff --git a/user/iplib/ptclbsum.c b/user/iplib/ptclbsum.c
new file mode 100755 (executable)
index 0000000..549d534
--- /dev/null
@@ -0,0 +1,80 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <iplib.h>
+
+static short   endian  = 1;
+static uint8_t*        aendian = (uint8_t*)&endian;
+#define        LITTLE  *aendian
+
+uint16_t
+ptclbsum(uint8_t *addr, int len)
+{
+       uint32_t losum, hisum, mdsum, x;
+       uint32_t t1, t2;
+
+       losum = 0;
+       hisum = 0;
+       mdsum = 0;
+
+       x = 0;
+       if((uintptr_t)addr & 1) {
+               if(len) {
+                       hisum += addr[0];
+                       len--;
+                       addr++;
+               }
+               x = 1;
+       }
+       while(len >= 16) {
+               t1 = *(uint16_t*)(addr+0);
+               t2 = *(uint16_t*)(addr+2);      mdsum += t1;
+               t1 = *(uint16_t*)(addr+4);      mdsum += t2;
+               t2 = *(uint16_t*)(addr+6);      mdsum += t1;
+               t1 = *(uint16_t*)(addr+8);      mdsum += t2;
+               t2 = *(uint16_t*)(addr+10);     mdsum += t1;
+               t1 = *(uint16_t*)(addr+12);     mdsum += t2;
+               t2 = *(uint16_t*)(addr+14);     mdsum += t1;
+               mdsum += t2;
+               len -= 16;
+               addr += 16;
+       }
+       while(len >= 2) {
+               mdsum += *(uint16_t*)addr;
+               len -= 2;
+               addr += 2;
+       }
+       if(x) {
+               if(len)
+                       losum += addr[0];
+               if(LITTLE)
+                       losum += mdsum;
+               else
+                       hisum += mdsum;
+       } else {
+               if(len)
+                       hisum += addr[0];
+               if(LITTLE)
+                       hisum += mdsum;
+               else
+                       losum += mdsum;
+       }
+
+       losum += hisum >> 8;
+       losum += (hisum & 0xff) << 8;
+       while(hisum = losum>>16)
+               losum = hisum + (losum & 0xffff);
+
+       return losum & 0xffff;
+}
diff --git a/user/iplib/readipifc.c b/user/iplib/readipifc.c
new file mode 100755 (executable)
index 0000000..89da938
--- /dev/null
@@ -0,0 +1,219 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <iplib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#define NFIELD 200
+#define nelem(x) (sizeof(x)/sizeof(x[0]))
+static struct ipifc**
+_readoldipifc(char *buf, struct ipifc **l, int index)
+{
+       char *f[NFIELD];
+       int i, n;
+       struct ipifc *ifc;
+       struct iplifc *lifc, **ll;
+
+       /* allocate new interface */
+       *l = ifc = calloc(sizeof(struct ipifc), 1);
+       if(ifc == NULL)
+               return l;
+       l = &ifc->next;
+       ifc->index = index;
+
+       n = tokenize(buf, f, NFIELD);
+       if(n < 2)
+               return l;
+
+       strncpy(ifc->dev, f[0], sizeof ifc->dev);
+       ifc->dev[sizeof(ifc->dev) - 1] = 0;
+       ifc->mtu = strtoul(f[1], NULL, 10);
+
+       ll = &ifc->lifc;
+       for(i = 2; n-i >= 7; i += 7){
+               /* allocate new local address */
+               *ll = lifc = calloc(sizeof(struct iplifc), 1);
+               ll = &lifc->next;
+
+               parseip(lifc->ip, f[i]);
+               parseipmask(lifc->mask, f[i+1]);
+               parseip(lifc->net, f[i+2]);
+               ifc->pktin = strtoul(f[i+3], NULL, 10);
+               ifc->pktout = strtoul(f[i+4], NULL, 10);
+               ifc->errin = strtoul(f[i+5], NULL, 10);
+               ifc->errout = strtoul(f[i+6], NULL, 10);
+       }
+       return l;
+}
+
+static char*
+findfield(char *name, char **f, int n)
+{
+       int i;
+
+       for(i = 0; i < n-1; i++)
+               if(strcmp(f[i], name) == 0)
+                       return f[i+1];
+       return "";
+}
+
+static struct ipifc**
+_readipifc(char *file, struct ipifc **l, int index)
+{
+       int i, n, fd, lines;
+       char buf[4*1024];
+       char *line[32];
+       char *f[64];
+       struct ipifc *ifc, **l0;
+       struct iplifc *lifc, **ll;
+
+       /* read the file */
+       fd = open(file, O_RDONLY);
+       if(fd < 0)
+               return l;
+       n = 0;
+       while((i = read(fd, buf+n, sizeof(buf)-1-n)) > 0 && n < sizeof(buf) - 1)
+               n += i;
+       buf[n] = 0;
+       close(fd);
+
+       if(strncmp(buf, "device", 6) != 0)
+               return _readoldipifc(buf, l, index);
+       /* ignore ifcs with no associated device */
+       if(strncmp(buf+6, "  ", 2) == 0)
+               return l;
+       /* allocate new interface */
+       *l = ifc = calloc(sizeof(struct ipifc), 1);
+       if(ifc == NULL)
+               return l;
+       l0 = l;
+       l = &ifc->next;
+       ifc->index = index;
+
+       lines = getfields(buf, line, nelem(line), 1, "\n");
+
+       /* pick off device specific info(first line) */
+       n = tokenize(line[0], f, nelem(f));
+       if(n%2 != 0)
+               goto lose;
+       strncpy(ifc->dev, findfield("device", f, n), sizeof(ifc->dev));
+       ifc->dev[sizeof(ifc->dev)-1] = 0;
+       if(ifc->dev[0] == 0){
+lose:
+               free(ifc);
+               *l0 = NULL;
+               return l;
+       }
+       ifc->mtu = strtoul(findfield("maxtu", f, n), NULL, 10);
+       ifc->sendra6 = atoi(findfield("sendra", f, n));
+       ifc->recvra6 = atoi(findfield("recvra", f, n));
+       ifc->rp.mflag = atoi(findfield("mflag", f, n));
+       ifc->rp.oflag = atoi(findfield("oflag", f, n));
+       ifc->rp.maxraint = atoi(findfield("maxraint", f, n));
+       ifc->rp.minraint = atoi(findfield("minraint", f, n));
+       ifc->rp.linkmtu = atoi(findfield("linkmtu", f, n));
+       ifc->rp.reachtime = atoi(findfield("reachtime", f, n));
+       ifc->rp.rxmitra = atoi(findfield("rxmitra", f, n));
+       ifc->rp.ttl = atoi(findfield("ttl", f, n));
+       ifc->rp.routerlt = atoi(findfield("routerlt", f, n));
+       ifc->pktin = strtoul(findfield("pktin", f, n), NULL, 10);
+       ifc->pktout = strtoul(findfield("pktout", f, n), NULL, 10);
+       ifc->errin = strtoul(findfield("errin", f, n), NULL, 10);
+       ifc->errout = strtoul(findfield("errout", f, n), NULL, 10);
+
+       /* now read the addresses */
+       ll = &ifc->lifc;
+       for(i = 1; i < lines; i++){
+               n = tokenize(line[i], f, nelem(f));
+               if(n < 5)
+                       break;
+
+               /* allocate new local address */
+               *ll = lifc = calloc(sizeof(struct iplifc), 1);
+               ll = &lifc->next;
+
+               parseip(lifc->ip, f[0]);
+               parseipmask(lifc->mask, f[1]);
+               parseip(lifc->net, f[2]);
+
+               lifc->validlt = strtoul(f[3], NULL, 10);
+               lifc->preflt = strtoul(f[4], NULL, 10);
+       }
+
+       return l;
+}
+
+static void
+_freeifc(struct ipifc *ifc)
+{
+       struct ipifc *next;
+       struct iplifc *lnext, *lifc;
+
+       if(ifc == NULL)
+               return;
+       for(; ifc; ifc = next){
+               next = ifc->next;
+               for(lifc = ifc->lifc; lifc; lifc = lnext){
+                       lnext = lifc->next;
+                       free(lifc);
+               }
+               free(ifc);
+       }
+}
+
+struct ipifc*
+readipifc(char *net, struct ipifc *ifc, int index)
+{
+       int fd, i, n;
+       struct dir *dir;
+       char directory[128];
+       char buf[128];
+       struct ipifc **l;
+
+       _freeifc(ifc);
+
+       l = &ifc;
+       ifc = NULL;
+
+       if(net == 0)
+               net = "/net";
+       snprintf(directory, sizeof(directory), "%s/ipifc", net);
+
+       if(index >= 0){
+               snprintf(buf, sizeof(buf), "%s/%d/status", directory, index);
+               _readipifc(buf, l, index);
+       } else {
+               DIR *d;
+               struct dirent *de;
+               d = opendir(directory);
+               if(! d)
+                       return NULL;
+               
+               while (de = readdir(d)){
+                       if(strcmp(de->d_name, "clone") == 0)
+                               continue;
+                       if(strcmp(de->d_name, "stats") == 0)
+                               continue;
+                       snprintf(buf, sizeof(buf), "%s/%s/status", directory, de->d_name);
+                       l = _readipifc(buf, l, atoi(de->d_name));
+               }
+               closedir(d);
+       }
+       
+       return ifc;
+}
diff --git a/user/iplib/scalar.cocci b/user/iplib/scalar.cocci
new file mode 100644 (file)
index 0000000..fed0fc6
--- /dev/null
@@ -0,0 +1,60 @@
+@@
+typedef uint;
+@@
+-uint
++unsigned int
+
+@@
+typedef uvlong;
+typedef uint64_t;
+@@
+-uvlong
++uint64_t
+
+@@
+typedef vlong;
+typedef int64_t;
+@@
+-vlong
++int64_t
+
+@@
+typedef ulong;
+typedef uint32_t;
+@@
+-ulong
++uint32_t
+
+@@
+typedef usize;
+@@
+-usize
++unsigned long
+
+@@
+typedef u32int;
+@@
+-u32int
++uint32_t
+
+@@
+typedef ushort;
+typedef uint16_t;
+@@
+-ushort
++uint16_t
+
+@@
+typedef uchar;
+typedef uint8_t;
+@@
+-uchar
++uint8_t
+
+@@
+typedef uintptr;
+typedef uintptr_t;
+@@
+-uintptr
++uintptr_t
+
diff --git a/user/iplib/tokenize.c b/user/iplib/tokenize.c
new file mode 100755 (executable)
index 0000000..ca4cad0
--- /dev/null
@@ -0,0 +1,128 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+/*
+
+ * Copyright 2013 Google Inc.
+ * Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories.
+ */
+
+#include <string.h>
+static char qsep[] = " \t\r\n";
+
+int sep(char *s)
+{
+       if (strchr(qsep, *s))
+               return 1;
+       return 0;
+}
+/* s is output string, t is input string.
+ * warning: modifies data in place.
+ */
+static char*
+qtoken(char *s, char *sep)
+{
+       int quoting;
+       char *t;
+
+       quoting = 0;
+       t = s;
+       while(*t!='\0' && (quoting || (strchr(sep,*t)==NULL))){
+               if(*t != '\''){
+                       *s++ = *t++;
+                       continue;
+               }
+               /* *t is a quote */
+               if(!quoting){
+                       quoting = 1;
+                       t++;
+                       continue;
+               }
+               /* quoting and we're on a quote */
+               if(t[1] != '\''){
+                       /* end of quoted section; absorb closing quote */
+                       t++;
+                       quoting = 0;
+                       continue;
+               }
+               /* doubled quote; fold one quote into two */
+               t++;
+               *s++ = *t++;
+       }
+       if(*s != '\0'){
+               *s = '\0';
+               if(t == s)
+                       t++;
+       }
+       return t;
+}
+
+static char*
+etoken(char *t, char *sep)
+{
+       int quoting;
+
+       /* move to end of next token */
+       quoting = 0;
+       while(*t!='\0' && (quoting || (strchr(sep,*t) == NULL))){
+               if(*t != '\''){
+                       t++;
+                       continue;
+               }
+               /* *t is a quote */
+               if(!quoting){
+                       quoting = 1;
+                       t++;
+                       continue;
+               }
+               /* quoting and we're on a quote */
+               if(t[1] != '\''){
+                       /* end of quoted section; absorb closing quote */
+                       t++;
+                       quoting = 0;
+                       continue;
+               }
+               /* doubled quote; fold one quote into two */
+               t += 2;
+       }
+       return t;
+}
+
+int
+gettokens(char *s, char **args, int maxargs, char *sep)
+{
+       int nargs;
+
+       for(nargs=0; nargs<maxargs; nargs++){
+               while((*s!='\0') && (strchr(sep, *s)!=NULL))
+                       *s++ = '\0';
+               if(*s == '\0')
+                       break;
+               args[nargs] = s;
+               s = etoken(s, sep);
+       }
+
+       return nargs;
+}
+
+int
+tokenize(char *s, char **args, int maxargs)
+{
+       int nargs;
+
+       for(nargs=0; nargs<maxargs; nargs++){
+               while((*s!='\0') && sep(s))
+                       s++;
+               if(*s == '\0')
+                       break;
+               args[nargs] = s;
+               s = qtoken(s, qsep);
+       }
+
+       return nargs;
+}
diff --git a/user/iplib/typedef.cocci b/user/iplib/typedef.cocci
new file mode 100644 (file)
index 0000000..36891d5
--- /dev/null
@@ -0,0 +1,405 @@
+@@
+typedef Ipifc;
+@@
+-Ipifc
++struct ipifc
+
+@@
+typedef Iplifc;
+@@
+-Iplifc
++struct iplifc
+
+@@
+typedef Ipv6rp;
+@@
+-Ipv6rp
++struct ipv6rp
+
+@@
+typedef Ip6hdr;
+@@
+-Ip6hdr
++struct ip6hdr
+
+@@
+typedef Icmp6hdr;
+@@
+-Icmp6hdr
++struct icmp6hdr
+
+@@
+typedef Udphdr;
+@@
+-Udphdr
++struct udphdr
+
+@@
+typedef Conv;
+@@
+-Conv
++struct conv
+
+@@
+typedef Fragment4;
+@@
+-Fragment4
++struct fragment4
+
+@@
+typedef Fragment6;
+@@
+-Fragment6
++struct fragment6
+
+@@
+typedef Fraghdr6;
+@@
+-Fraghdr6
++struct fraghdr6
+
+@@
+typedef Fs;
+@@
+-Fs
++struct fs
+
+@@
+typedef Hwaddr;
+@@
+-Hwaddr
++struct hwaddr
+
+@@
+typedef IP;
+@@
+-IP
++struct IP
+
+@@
+typedef IPaux;
+@@
+-IPaux
++struct IPaux
+
+@@
+typedef Ip4hdr;
+@@
+-Ip4hdr
++struct Ip4hdr
+
+@@
+typedef Ipfrag;
+@@
+-Ipfrag
++struct Ipfrag
+
+@@
+typedef Ipself;
+@@
+-Ipself
++struct Ipself
+
+@@
+typedef Ipselftab;
+@@
+-Ipselftab
++struct Ipselftab
+
+@@
+typedef Iplink;
+@@
+-Iplink
++struct Iplink
+
+@@
+typedef Ipmulti;
+@@
+-Ipmulti
++struct Ipmulti
+
+@@
+typedef Iphash;
+@@
+-Iphash
++struct iphash
+
+@@
+typedef Ipht;
+@@
+-Ipht
++struct Ipht
+
+@@
+typedef Netlog;
+@@
+-Netlog
++struct netlog
+
+@@
+typedef Medium;
+@@
+-Medium
++struct medium
+
+@@
+typedef Proto;
+@@
+-Proto
++struct proto
+
+@@
+typedef Arpent;
+@@
+-Arpent
++struct arpent
+
+@@
+typedef Arp;
+@@
+-Arp
++struct arp
+
+@@
+typedef Route;
+@@
+-Route
++struct route
+
+@@
+typedef Routerparams;
+@@
+-Routerparams
++struct routerparams
+
+@@
+typedef Hostparams;
+@@
+-Hostparams
++struct hostparams
+
+@@
+typedef v6router;
+@@
+-v6router
++struct v6router
+
+@@
+typedef v6params;
+@@
+-v6params
++struct v6params
+
+@@
+typedef RouteTree;
+@@
+-RouteTree
++struct RouteTree
+
+@@
+typedef Routewalk;
+@@
+-Routewalk
++struct routewalk
+
+@@
+typedef Queue;
+@@
+-Queue
++struct queue
+
+@@
+typedef V4route;
+@@
+-V4route
++struct V4route
+
+@@
+typedef V6route;
+@@
+-V6route
++struct V6route
+
+
+@@
+typedef Block;
+@@
+-Block
++struct block
+
+@@
+typedef Ctlr;
+@@
+-Ctlr
++struct ctlr
+
+@@
+typedef Ether;
+@@
+-Ether
++struct ether
+
+@@
+typedef Etherpkt;
+@@
+-Etherpkt
++struct etherpkt
+
+@@
+typedef Chan;
+@@
+-Chan
++struct chan
+
+@@
+typedef Dirtab;
+@@
+-Dirtab
++struct dirtab
+
+@@
+typedef Dirlist;
+@@
+-Dirlist
++struct dirlist
+
+@@
+typedef Dir;
+@@
+-Dir
++struct dir
+
+@@
+typedef Fgrp;
+@@
+-Fgrp
++struct fgrp
+
+@@
+typedef Qid;
+@@
+-Qid
++struct qid
+
+@@
+typedef Waitqid;
+@@
+-Waitqid
++struct waitqid
+
+@@
+typedef Mount;
+@@
+-Mount
++struct mount
+
+@@
+typedef Mhead;
+@@
+-Mhead
++struct mhead
+
+@@
+typedef Path;
+@@
+-Path
++struct path
+
+@@
+typedef Proc;
+@@
+-Proc
++struct proc
+
+@@
+typedef Pgrp;
+@@
+-Pgrp
++struct pgrp
+
+@@
+typedef Dev;
+@@
+-Dev
++struct dev
+
+@@
+typedef QLock;
+typedef qlock_t;
+@@
+-QLock
++qlock_t 
+
+@@
+typedef RWLock;
+typedef rwlock_t;
+@@
+-RWLock
++rwlock_t 
+
+@@
+typedef Lock;
+typedef spinlock_t;
+@@
+-Lock
++spinlock_t 
+
+@@
+typedef Ref;
+@@
+-Ref
++struct kref
+
+@@
+typedef Walkqid;
+@@
+-Walkqid
++struct walkqid
+
+@@
+typedef Cmdbuf;
+@@
+-Cmdbuf
++struct cmdbuf
+
+@@
+typedef Cmdtab;
+@@
+-Cmdtab
++struct cmdtab
+
+@@
+typedef Tcppriv;
+@@
+-Tcppriv
++struct tcppriv
+
+@@
+typedef Mnt;
+@@
+-Mnt
++struct mnt
+
+@@
+typedef Mntwalk;
+@@
+-Mntwalk
++struct mntwalk
+
+@@
+typedef Mntrpc;
+@@
+-Mntrpc
++struct mntrpc
+
+@@
+typedef Fcall;
+@@
+-Fcall
++struct fcall
+
+@@
+typedef Srv;
+@@
+-Srv
++struct srv
diff --git a/user/ndblib/Makefile b/user/ndblib/Makefile
new file mode 100644 (file)
index 0000000..22e3e9f
--- /dev/null
@@ -0,0 +1,50 @@
+ARCH ?= none   # catch bugs
+CFLAGS_USER += -static -fomit-frame-pointer -g
+LIBNAME = ndb
+
+SRCDIR := 
+OBJDIR := $(SRCDIR)obj
+INCDIR = $(SRCDIR)include
+
+INCS = -I. -I$(INCDIR)
+FINALLIB = $(OBJDIR)/lib$(LIBNAME).a
+
+uc = $(shell echo $(1) | tr a-z A-Z)
+
+LIBUCNAME := $(call uc, $(LIBNAME))
+HEADERS := $(shell find $(INCDIR) -name *.h)
+CFILES  := $(wildcard $(SRCDIR)*.c)
+CFILES  += $(wildcard $(SRCDIR)$(ARCH)/*.c)
+SFILES  := $(wildcard $(SRCDIR)$(ARCH)/*.S)
+OBJS    := $(patsubst %.c, $(OBJDIR)/%.o, $(CFILES)) \
+           $(patsubst %.S, $(OBJDIR)/%.o, $(SFILES))
+
+all: $(FINALLIB)
+       @:
+
+$(OBJDIR)/$(ARCH)/%.o: $(SRCDIR)$(ARCH)/%.S $(HEADERS)
+       @echo + as [$(LIBUCNAME)] $<
+       @mkdir -p $(@D)
+       $(Q)$(CC) $(CFLAGS_USER) $(INCS) -o $@ -c $<
+
+$(OBJDIR)/%.o: $(SRCDIR)%.c $(HEADERS)
+       @echo + cc [$(LIBUCNAME)] $<
+       @mkdir -p $(@D)
+       $(Q)$(CC) $(CFLAGS_USER) $(INCS) -o $@ -c $<
+
+$(FINALLIB): $(OBJS)
+       @echo + ar [$(LIBUCNAME)] $@
+       @mkdir -p $(@D)
+       $(Q)$(AR) rc $@ $(OBJS)
+
+install: $(FINALLIB)
+       @cp $(FINALLIB) $(XCC_TARGET_ROOT)/lib/
+       @cp -R $(INCDIR)/* $(XCC_TARGET_ROOT)/sys-include/
+       @rm -rf $(XCC_TARGET_ROOT)/sys-include/libip  
+       @ln -fs . $(XCC_TARGET_ROOT)/sys-include/libip  
+
+clean: 
+       @echo + clean [$(LIBUCNAME)]
+       $(Q)rm -rf $(FINALLIB)
+       $(Q)rm -rf $(OBJDIR)
+
diff --git a/user/ndblib/convD2M.c b/user/ndblib/convD2M.c
new file mode 100644 (file)
index 0000000..897e4af
--- /dev/null
@@ -0,0 +1,111 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <libip.h>
+#include <dir.h>
+#include <ndb.h>
+#include <fcall.h>
+
+unsigned int
+sizeD2M(struct dir *d)
+{
+       char *sv[4];
+       int i, ns;
+
+       sv[0] = d->name;
+       sv[1] = d->uid;
+       sv[2] = d->gid;
+       sv[3] = d->muid;
+
+       ns = 0;
+       for(i = 0; i < 4; i++)
+               if(sv[i])
+                       ns += strlen(sv[i]);
+
+       return STATFIXLEN + ns;
+}
+
+unsigned int
+convD2M(struct dir *d, uint8_t *buf, unsigned int nbuf)
+{
+       uint8_t *p, *ebuf;
+       char *sv[4];
+       int i, ns, nsv[4], ss;
+
+       if(nbuf < BIT16SZ)
+               return 0;
+
+       p = buf;
+       ebuf = buf + nbuf;
+
+       sv[0] = d->name;
+       sv[1] = d->uid;
+       sv[2] = d->gid;
+       sv[3] = d->muid;
+
+       ns = 0;
+       for(i = 0; i < 4; i++){
+               if(sv[i])
+                       nsv[i] = strlen(sv[i]);
+               else
+                       nsv[i] = 0;
+               ns += nsv[i];
+       }
+
+       ss = STATFIXLEN + ns;
+
+       /* set size before erroring, so user can know how much is needed */
+       /* note that length excludes count field itself */
+       PBIT16(p, ss-BIT16SZ);
+       p += BIT16SZ;
+
+       if(ss > nbuf)
+               return BIT16SZ;
+
+       PBIT16(p, d->type);
+       p += BIT16SZ;
+       PBIT32(p, d->dev);
+       p += BIT32SZ;
+       PBIT8(p, d->qid.type);
+       p += BIT8SZ;
+       PBIT32(p, d->qid.vers);
+       p += BIT32SZ;
+       PBIT64(p, d->qid.path);
+       p += BIT64SZ;
+       PBIT32(p, d->mode);
+       p += BIT32SZ;
+       PBIT32(p, d->atime);
+       p += BIT32SZ;
+       PBIT32(p, d->mtime);
+       p += BIT32SZ;
+       PBIT64(p, d->length);
+       p += BIT64SZ;
+
+       for(i = 0; i < 4; i++){
+               ns = nsv[i];
+               if(p + ns + BIT16SZ > ebuf)
+                       return 0;
+               PBIT16(p, ns);
+               p += BIT16SZ;
+               if(ns)
+                       memmove(p, sv[i], ns);
+               p += ns;
+       }
+
+       if(ss != p - buf)
+               return 0;
+
+       return p - buf;
+}
diff --git a/user/ndblib/convM2D.c b/user/ndblib/convM2D.c
new file mode 100644 (file)
index 0000000..78fe514
--- /dev/null
@@ -0,0 +1,110 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <libip.h>
+#include <dir.h>
+#include <ndb.h>
+#include <fcall.h>
+
+int
+statcheck(uint8_t *buf, unsigned int nbuf)
+{
+       uint8_t *ebuf;
+       int i;
+
+       ebuf = buf + nbuf;
+
+       if(nbuf < STATFIXLEN || nbuf != BIT16SZ + GBIT16(buf))
+               return -1;
+
+       buf += STATFIXLEN - 4 * BIT16SZ;
+
+       for(i = 0; i < 4; i++){
+               if(buf + BIT16SZ > ebuf)
+                       return -1;
+               buf += BIT16SZ + GBIT16(buf);
+       }
+
+       if(buf != ebuf)
+               return -1;
+
+       return 0;
+}
+
+static char nullstring[] = "";
+
+unsigned int
+convM2D(uint8_t *buf, unsigned int nbuf, struct dir *d, char *strs)
+{
+       uint8_t *p, *ebuf;
+       char *sv[4];
+       int i, ns;
+
+       if(nbuf < STATFIXLEN)
+               return 0; 
+
+       p = buf;
+       ebuf = buf + nbuf;
+
+       p += BIT16SZ;   /* ignore size */
+       d->type = GBIT16(p);
+       p += BIT16SZ;
+       d->dev = GBIT32(p);
+       p += BIT32SZ;
+       d->qid.type = GBIT8(p);
+       p += BIT8SZ;
+       d->qid.vers = GBIT32(p);
+       p += BIT32SZ;
+       d->qid.path = GBIT64(p);
+       p += BIT64SZ;
+       d->mode = GBIT32(p);
+       p += BIT32SZ;
+       d->atime = GBIT32(p);
+       p += BIT32SZ;
+       d->mtime = GBIT32(p);
+       p += BIT32SZ;
+       d->length = GBIT64(p);
+       p += BIT64SZ;
+
+       for(i = 0; i < 4; i++){
+               if(p + BIT16SZ > ebuf)
+                       return 0;
+               ns = GBIT16(p);
+               p += BIT16SZ;
+               if(p + ns > ebuf)
+                       return 0;
+               if(strs){
+                       sv[i] = strs;
+                       memmove(strs, p, ns);
+                       strs += ns;
+                       *strs++ = '\0';
+               }
+               p += ns;
+       }
+
+       if(strs){
+               d->name = sv[0];
+               d->uid = sv[1];
+               d->gid = sv[2];
+               d->muid = sv[3];
+       }else{
+               d->name = nullstring;
+               d->uid = nullstring;
+               d->gid = nullstring;
+               d->muid = nullstring;
+       }
+       
+       return p - buf;
+}
diff --git a/user/ndblib/convM2S.c b/user/ndblib/convM2S.c
new file mode 100755 (executable)
index 0000000..6cef82f
--- /dev/null
@@ -0,0 +1,331 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <libip.h>
+#include <dir.h>
+#include <ndb.h>
+#include <fcall.h>
+
+static
+uint8_t*
+gstring(uint8_t *p, uint8_t *ep, char **s)
+{
+       unsigned int n;
+
+       if(p+BIT16SZ > ep)
+               return NULL;
+       n = GBIT16(p);
+       p += BIT16SZ - 1;
+       if(p+n+1 > ep)
+               return NULL;
+       /* move it down, on top of count, to make room for '\0' */
+       memmove(p, p + 1, n);
+       p[n] = '\0';
+       *s = (char*)p;
+       p += n+1;
+       return p;
+}
+
+static
+uint8_t*
+gqid(uint8_t *p, uint8_t *ep, struct qid *q)
+{
+       if(p+QIDSZ > ep)
+               return NULL;
+       q->type = GBIT8(p);
+       p += BIT8SZ;
+       q->vers = GBIT32(p);
+       p += BIT32SZ;
+       q->path = GBIT64(p);
+       p += BIT64SZ;
+       return p;
+}
+
+/*
+ * no syntactic checks.
+ * three causes for error:
+ *  1. message size field is incorrect
+ *  2. input buffer too short for its own data (counts too long, etc.)
+ *  3. too many names or qids
+ * gqid() and gstring() return NULL if they would reach beyond buffer.
+ * main switch statement checks range and also can fall through
+ * to test at end of routine.
+ */
+unsigned int
+convM2S(uint8_t *ap, unsigned int nap, struct fcall *f)
+{
+       uint8_t *p, *ep;
+       unsigned int i, size;
+
+       p = ap;
+       ep = p + nap;
+
+       if(p+BIT32SZ+BIT8SZ+BIT16SZ > ep)
+               return 0;
+       size = GBIT32(p);
+       p += BIT32SZ;
+
+       if(size < BIT32SZ+BIT8SZ+BIT16SZ)
+               return 0;
+
+       f->type = GBIT8(p);
+       p += BIT8SZ;
+       f->tag = GBIT16(p);
+       p += BIT16SZ;
+
+       switch(f->type)
+       {
+       default:
+               return 0;
+
+       case Tversion:
+               if(p+BIT32SZ > ep)
+                       return 0;
+               f->msize = GBIT32(p);
+               p += BIT32SZ;
+               p = gstring(p, ep, &f->version);
+               break;
+
+       case Tflush:
+               if(p+BIT16SZ > ep)
+                       return 0;
+               f->oldtag = GBIT16(p);
+               p += BIT16SZ;
+               break;
+
+       case Tauth:
+               if(p+BIT32SZ > ep)
+                       return 0;
+               f->afid = GBIT32(p);
+               p += BIT32SZ;
+               p = gstring(p, ep, &f->uname);
+               if(p == NULL)
+                       break;
+               p = gstring(p, ep, &f->aname);
+               if(p == NULL)
+                       break;
+               break;
+
+       case Tattach:
+               if(p+BIT32SZ > ep)
+                       return 0;
+               f->fid = GBIT32(p);
+               p += BIT32SZ;
+               if(p+BIT32SZ > ep)
+                       return 0;
+               f->afid = GBIT32(p);
+               p += BIT32SZ;
+               p = gstring(p, ep, &f->uname);
+               if(p == NULL)
+                       break;
+               p = gstring(p, ep, &f->aname);
+               if(p == NULL)
+                       break;
+               break;
+
+       case Twalk:
+               if(p+BIT32SZ+BIT32SZ+BIT16SZ > ep)
+                       return 0;
+               f->fid = GBIT32(p);
+               p += BIT32SZ;
+               f->newfid = GBIT32(p);
+               p += BIT32SZ;
+               f->nwname = GBIT16(p);
+               p += BIT16SZ;
+               if(f->nwname > MAXWELEM)
+                       return 0;
+               for(i=0; i<f->nwname; i++){
+                       p = gstring(p, ep, &f->wname[i]);
+                       if(p == NULL)
+                               break;
+               }
+               break;
+
+       case Topen:
+               if(p+BIT32SZ+BIT8SZ > ep)
+                       return 0;
+               f->fid = GBIT32(p);
+               p += BIT32SZ;
+               f->mode = GBIT8(p);
+               p += BIT8SZ;
+               break;
+
+       case Tcreate:
+               if(p+BIT32SZ > ep)
+                       return 0;
+               f->fid = GBIT32(p);
+               p += BIT32SZ;
+               p = gstring(p, ep, &f->name);
+               if(p == NULL)
+                       break;
+               if(p+BIT32SZ+BIT8SZ > ep)
+                       return 0;
+               f->perm = GBIT32(p);
+               p += BIT32SZ;
+               f->mode = GBIT8(p);
+               p += BIT8SZ;
+               break;
+
+       case Tread:
+               if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep)
+                       return 0;
+               f->fid = GBIT32(p);
+               p += BIT32SZ;
+               f->offset = GBIT64(p);
+               p += BIT64SZ;
+               f->count = GBIT32(p);
+               p += BIT32SZ;
+               break;
+
+       case Twrite:
+               if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep)
+                       return 0;
+               f->fid = GBIT32(p);
+               p += BIT32SZ;
+               f->offset = GBIT64(p);
+               p += BIT64SZ;
+               f->count = GBIT32(p);
+               p += BIT32SZ;
+               if(p+f->count > ep)
+                       return 0;
+               f->data = (char*)p;
+               p += f->count;
+               break;
+
+       case Tclunk:
+       case Tremove:
+               if(p+BIT32SZ > ep)
+                       return 0;
+               f->fid = GBIT32(p);
+               p += BIT32SZ;
+               break;
+
+       case Tstat:
+               if(p+BIT32SZ > ep)
+                       return 0;
+               f->fid = GBIT32(p);
+               p += BIT32SZ;
+               break;
+
+       case Twstat:
+               if(p+BIT32SZ+BIT16SZ > ep)
+                       return 0;
+               f->fid = GBIT32(p);
+               p += BIT32SZ;
+               f->nstat = GBIT16(p);
+               p += BIT16SZ;
+               if(p+f->nstat > ep)
+                       return 0;
+               f->stat = p;
+               p += f->nstat;
+               break;
+
+/*
+ */
+       case Rversion:
+               if(p+BIT32SZ > ep)
+                       return 0;
+               f->msize = GBIT32(p);
+               p += BIT32SZ;
+               p = gstring(p, ep, &f->version);
+               break;
+
+       case Rerror:
+               p = gstring(p, ep, &f->ename);
+               break;
+
+       case Rflush:
+               break;
+
+       case Rauth:
+               p = gqid(p, ep, &f->aqid);
+               if(p == NULL)
+                       break;
+               break;
+
+       case Rattach:
+               p = gqid(p, ep, &f->qid);
+               if(p == NULL)
+                       break;
+               break;
+
+       case Rwalk:
+               if(p+BIT16SZ > ep)
+                       return 0;
+               f->nwqid = GBIT16(p);
+               p += BIT16SZ;
+               if(f->nwqid > MAXWELEM)
+                       return 0;
+               for(i=0; i<f->nwqid; i++){
+                       p = gqid(p, ep, &f->wqid[i]);
+                       if(p == NULL)
+                               break;
+               }
+               break;
+
+       case Ropen:
+       case Rcreate:
+               p = gqid(p, ep, &f->qid);
+               if(p == NULL)
+                       break;
+               if(p+BIT32SZ > ep)
+                       return 0;
+               f->iounit = GBIT32(p);
+               p += BIT32SZ;
+               break;
+
+       case Rread:
+               if(p+BIT32SZ > ep)
+                       return 0;
+               f->count = GBIT32(p);
+               p += BIT32SZ;
+               if(p+f->count > ep)
+                       return 0;
+               f->data = (char*)p;
+               p += f->count;
+               break;
+
+       case Rwrite:
+               if(p+BIT32SZ > ep)
+                       return 0;
+               f->count = GBIT32(p);
+               p += BIT32SZ;
+               break;
+
+       case Rclunk:
+       case Rremove:
+               break;
+
+       case Rstat:
+               if(p+BIT16SZ > ep)
+                       return 0;
+               f->nstat = GBIT16(p);
+               p += BIT16SZ;
+               if(p+f->nstat > ep)
+                       return 0;
+               f->stat = p;
+               p += f->nstat;
+               break;
+
+       case Rwstat:
+               break;
+       }
+
+       if(p==NULL || p>ep)
+               return 0;
+       if(ap+size == p)
+               return size;
+       return 0;
+}
diff --git a/user/ndblib/convS2M.c b/user/ndblib/convS2M.c
new file mode 100755 (executable)
index 0000000..5c46001
--- /dev/null
@@ -0,0 +1,405 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <libip.h>
+#include <dir.h>
+#include <ndb.h>
+#include <fcall.h>
+
+static
+uint8_t*
+pstring(uint8_t *p, char *s)
+{
+       unsigned int n;
+
+       if(s == NULL){
+               PBIT16(p, 0);
+               p += BIT16SZ;
+               return p;
+       }
+
+       n = strlen(s);
+       /*
+        * We are moving the string before the length,
+        * so you can S2M a struct into an existing message
+        */
+       memmove(p + BIT16SZ, s, n);
+       PBIT16(p, n);
+       p += n + BIT16SZ;
+       return p;
+}
+
+static
+uint8_t*
+pqid(uint8_t *p, struct qid *q)
+{
+       PBIT8(p, q->type);
+       p += BIT8SZ;
+       PBIT32(p, q->vers);
+       p += BIT32SZ;
+       PBIT64(p, q->path);
+       p += BIT64SZ;
+       return p;
+}
+
+static
+unsigned int
+stringsz(char *s)
+{
+       if(s == NULL)
+               return BIT16SZ;
+
+       return BIT16SZ+strlen(s);
+}
+
+unsigned int
+sizeS2M(struct fcall *f)
+{
+       unsigned int n;
+       int i;
+
+       n = 0;
+       n += BIT32SZ;   /* size */
+       n += BIT8SZ;    /* type */
+       n += BIT16SZ;   /* tag */
+
+       switch(f->type)
+       {
+       default:
+               return 0;
+
+       case Tversion:
+               n += BIT32SZ;
+               n += stringsz(f->version);
+               break;
+
+       case Tflush:
+               n += BIT16SZ;
+               break;
+
+       case Tauth:
+               n += BIT32SZ;
+               n += stringsz(f->uname);
+               n += stringsz(f->aname);
+               break;
+
+       case Tattach:
+               n += BIT32SZ;
+               n += BIT32SZ;
+               n += stringsz(f->uname);
+               n += stringsz(f->aname);
+               break;
+
+       case Twalk:
+               n += BIT32SZ;
+               n += BIT32SZ;
+               n += BIT16SZ;
+               for(i=0; i<f->nwname; i++)
+                       n += stringsz(f->wname[i]);
+               break;
+
+       case Topen:
+               n += BIT32SZ;
+               n += BIT8SZ;
+               break;
+
+       case Tcreate:
+               n += BIT32SZ;
+               n += stringsz(f->name);
+               n += BIT32SZ;
+               n += BIT8SZ;
+               break;
+
+       case Tread:
+               n += BIT32SZ;
+               n += BIT64SZ;
+               n += BIT32SZ;
+               break;
+
+       case Twrite:
+               n += BIT32SZ;
+               n += BIT64SZ;
+               n += BIT32SZ;
+               n += f->count;
+               break;
+
+       case Tclunk:
+       case Tremove:
+               n += BIT32SZ;
+               break;
+
+       case Tstat:
+               n += BIT32SZ;
+               break;
+
+       case Twstat:
+               n += BIT32SZ;
+               n += BIT16SZ;
+               n += f->nstat;
+               break;
+/*
+ */
+
+       case Rversion:
+               n += BIT32SZ;
+               n += stringsz(f->version);
+               break;
+
+       case Rerror:
+               n += stringsz(f->ename);
+               break;
+
+       case Rflush:
+               break;
+
+       case Rauth:
+               n += QIDSZ;
+               break;
+
+       case Rattach:
+               n += QIDSZ;
+               break;
+
+       case Rwalk:
+               n += BIT16SZ;
+               n += f->nwqid*QIDSZ;
+               break;
+
+       case Ropen:
+       case Rcreate:
+               n += QIDSZ;
+               n += BIT32SZ;
+               break;
+
+       case Rread:
+               n += BIT32SZ;
+               n += f->count;
+               break;
+
+       case Rwrite:
+               n += BIT32SZ;
+               break;
+
+       case Rclunk:
+               break;
+
+       case Rremove:
+               break;
+
+       case Rstat:
+               n += BIT16SZ;
+               n += f->nstat;
+               break;
+
+       case Rwstat:
+               break;
+       }
+       return n;
+}
+
+unsigned int
+convS2M(struct fcall *f, uint8_t *ap, unsigned int nap)
+{
+       uint8_t *p;
+       unsigned int i, size;
+
+       size = sizeS2M(f);
+       if(size == 0)
+               return 0;
+       if(size > nap)
+               return 0;
+
+       p = (uint8_t*)ap;
+
+       PBIT32(p, size);
+       p += BIT32SZ;
+       PBIT8(p, f->type);
+       p += BIT8SZ;
+       PBIT16(p, f->tag);
+       p += BIT16SZ;
+
+       switch(f->type)
+       {
+       default:
+               return 0;
+
+       case Tversion:
+               PBIT32(p, f->msize);
+               p += BIT32SZ;
+               p = pstring(p, f->version);
+               break;
+
+       case Tflush:
+               PBIT16(p, f->oldtag);
+               p += BIT16SZ;
+               break;
+
+       case Tauth:
+               PBIT32(p, f->afid);
+               p += BIT32SZ;
+               p  = pstring(p, f->uname);
+               p  = pstring(p, f->aname);
+               break;
+
+       case Tattach:
+               PBIT32(p, f->fid);
+               p += BIT32SZ;
+               PBIT32(p, f->afid);
+               p += BIT32SZ;
+               p  = pstring(p, f->uname);
+               p  = pstring(p, f->aname);
+               break;
+
+       case Twalk:
+               PBIT32(p, f->fid);
+               p += BIT32SZ;
+               PBIT32(p, f->newfid);
+               p += BIT32SZ;
+               PBIT16(p, f->nwname);
+               p += BIT16SZ;
+               if(f->nwname > MAXWELEM)
+                       return 0;
+               for(i=0; i<f->nwname; i++)
+                       p = pstring(p, f->wname[i]);
+               break;
+
+       case Topen:
+               PBIT32(p, f->fid);
+               p += BIT32SZ;
+               PBIT8(p, f->mode);
+               p += BIT8SZ;
+               break;
+
+       case Tcreate:
+               PBIT32(p, f->fid);
+               p += BIT32SZ;
+               p = pstring(p, f->name);
+               PBIT32(p, f->perm);
+               p += BIT32SZ;
+               PBIT8(p, f->mode);
+               p += BIT8SZ;
+               break;
+
+       case Tread:
+               PBIT32(p, f->fid);
+               p += BIT32SZ;
+               PBIT64(p, f->offset);
+               p += BIT64SZ;
+               PBIT32(p, f->count);
+               p += BIT32SZ;
+               break;
+
+       case Twrite:
+               PBIT32(p, f->fid);
+               p += BIT32SZ;
+               PBIT64(p, f->offset);
+               p += BIT64SZ;
+               PBIT32(p, f->count);
+               p += BIT32SZ;
+               memmove(p, f->data, f->count);
+               p += f->count;
+               break;
+
+       case Tclunk:
+       case Tremove:
+               PBIT32(p, f->fid);
+               p += BIT32SZ;
+               break;
+
+       case Tstat:
+               PBIT32(p, f->fid);
+               p += BIT32SZ;
+               break;
+
+       case Twstat:
+               PBIT32(p, f->fid);
+               p += BIT32SZ;
+               PBIT16(p, f->nstat);
+               p += BIT16SZ;
+               memmove(p, f->stat, f->nstat);
+               p += f->nstat;
+               break;
+/*
+ */
+
+       case Rversion:
+               PBIT32(p, f->msize);
+               p += BIT32SZ;
+               p = pstring(p, f->version);
+               break;
+
+       case Rerror:
+               p = pstring(p, f->ename);
+               break;
+
+       case Rflush:
+               break;
+
+       case Rauth:
+               p = pqid(p, &f->aqid);
+               break;
+
+       case Rattach:
+               p = pqid(p, &f->qid);
+               break;
+
+       case Rwalk:
+               PBIT16(p, f->nwqid);
+               p += BIT16SZ;
+               if(f->nwqid > MAXWELEM)
+                       return 0;
+               for(i=0; i<f->nwqid; i++)
+                       p = pqid(p, &f->wqid[i]);
+               break;
+
+       case Ropen:
+       case Rcreate:
+               p = pqid(p, &f->qid);
+               PBIT32(p, f->iounit);
+               p += BIT32SZ;
+               break;
+
+       case Rread:
+               PBIT32(p, f->count);
+               p += BIT32SZ;
+               memmove(p, f->data, f->count);
+               p += f->count;
+               break;
+
+       case Rwrite:
+               PBIT32(p, f->count);
+               p += BIT32SZ;
+               break;
+
+       case Rclunk:
+               break;
+
+       case Rremove:
+               break;
+
+       case Rstat:
+               PBIT16(p, f->nstat);
+               p += BIT16SZ;
+               memmove(p, f->stat, f->nstat);
+               p += f->nstat;
+               break;
+
+       case Rwstat:
+               break;
+       }
+       if(size != p-ap)
+               return 0;
+       return size;
+}
diff --git a/user/ndblib/csgetval.c b/user/ndblib/csgetval.c
new file mode 100755 (executable)
index 0000000..bb6ffa2
--- /dev/null
@@ -0,0 +1,120 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <libip.h>
+#include <ndb.h>
+
+/*
+ *  search for a tuple that has the given 'attr=val' and also 'rattr=x'.
+ *  copy 'x' into 'buf' and return the whole tuple.
+ *
+ *  return 0 if not found.
+ */
+char*
+csgetvalue(char *netroot, char *attr, char *val, char *rattr,
+          struct ndbtuple **pp)
+{
+       struct ndbtuple *t, *first, *last;
+       int n, linefound;
+       char line[1024];
+       int fd;
+       int oops = 0;
+       char *rv;
+
+       if(pp)
+               *pp = NULL;
+       rv = NULL;
+
+       if(netroot)
+               snprintf(line, sizeof(line), "%s/cs", netroot);
+       else
+               strcpy(line, "/net/cs");
+       fd = open(line, O_RDWR);
+       if(fd < 0)
+               return 0;
+       lseek(fd, 0, 0);
+       snprintf(line, sizeof(line), "!%s=%s %s=*", attr, val, rattr);
+       if(write(fd, line, strlen(line)) < 0){
+               close(fd);
+               return 0;
+       }
+       lseek(fd, 0, 0);
+
+       first = last = 0;
+       linefound = 0;
+       for(;;){
+               n = read(fd, line, sizeof(line)-2);
+               if(n <= 0)
+                       break;
+               line[n] = '\n';
+               line[n+1] = 0;
+
+               t = _ndbparseline(line);
+               if(t == 0)
+                       continue;
+               if(first)
+                       last->entry = t;
+               else
+                       first = t;
+               last = t;
+
+               while(last->entry)
+                       last = last->entry;
+
+               for(; t; t = t->entry){
+                       if(linefound == 0){
+                               if(strcmp(rattr, t->attr) == 0){
+                                       linefound = 1;
+                                       rv = strdup(t->val);
+                               }
+                       }
+               }
+       }
+       close(fd);
+
+       if(oops){
+               werrstr("buffer too short");
+               ndbfree(first);
+               return NULL;
+       }
+
+       if(pp){
+               *pp = first;
+       } else
+               ndbfree(first);
+
+       return rv;
+}
+
+struct ndbtuple*
+csgetval(char *netroot, char *attr, char *val, char *rattr, char *buf)
+{
+       struct ndbtuple *t;
+       char *p;
+
+       p = csgetvalue(netroot, attr, val, rattr, &t);
+       if(p == NULL){
+               if(buf != NULL)
+                       *buf = 0;
+       } else {
+               if(buf != NULL){
+                       strncpy(buf, p, Ndbvlen-1);
+                       buf[Ndbvlen-1] = 0;
+               }
+               free(p);
+       }
+       ndbsetmalloctag(t, getcallerpc(&netroot));
+       return t;
+}
diff --git a/user/ndblib/csipinfo.c b/user/ndblib/csipinfo.c
new file mode 100755 (executable)
index 0000000..f32f416
--- /dev/null
@@ -0,0 +1,87 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <libip.h>
+#include <ndb.h>
+
+/*
+ *  look up the ip attributes 'list' for an entry that has the
+ *  given 'attr=val' and a 'ip=' tuples.
+ *
+ *  return NULL if not found.
+ */
+struct ndbtuple*
+csipinfo(char *netroot, char *attr, char *val, char **list, int n)
+{
+       struct ndbtuple *t, *first, *last;
+       int i;
+       char line[1024];
+       int fd;
+       char *p, *e;
+       int left,amt;
+
+       if(netroot)
+               snprintf(line, sizeof(line), "%s/cs", netroot);
+       else
+               strcpy(line, "/net/cs");
+       fd = open(line, O_RDWR);
+       if(fd < 0)
+               return 0;
+       lseek(fd, 0, 0);
+       left = sizeof(line);
+       amt = snprintf(line, left, "!ipinfo %s=%s", attr, val);
+       if(amt < 0)
+               return 0;
+       left -= amt;
+       for(i = 0; i < n; i++){
+               if(*list == NULL)
+                       break;
+               amt = snprintf(p, left, " %s", *list++);
+               if(amt < 0)
+                       return 0;
+               left -= amt;
+       }
+       
+       if(write(fd, line, strlen(line)) < 0){
+               close(fd);
+               return 0;
+       }
+       lseek(fd, 0, 0);
+
+       first = last = 0;
+       for(;;){
+               n = read(fd, line, sizeof(line)-2);
+               if(n <= 0)
+                       break;
+               line[n] = '\n';
+               line[n+1] = 0;
+
+               t = _ndbparseline(line);
+               if(t == 0)
+                       continue;
+               if(first)
+                       last->entry = t;
+               else
+                       first = t;
+               last = t;
+
+               while(last->entry)
+                       last = last->entry;
+       }
+       close(fd);
+
+       ndbsetmalloctag(first, getcallerpc(&netroot));
+       return first;
+}
diff --git a/user/ndblib/dnsquery.c b/user/ndblib/dnsquery.c
new file mode 100755 (executable)
index 0000000..0bda98f
--- /dev/null
@@ -0,0 +1,184 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <libip.h>
+#include <ndb.h>
+
+static void nstrcpy(char*, char*, int);
+static void mkptrname(char*, char*, int);
+static struct ndbtuple *doquery(int, char *dn, char *type);
+
+/*
+ *  search for a tuple that has the given 'attr=val' and also 'rattr=x'.
+ *  copy 'x' into 'buf' and return the whole tuple.
+ *
+ *  return 0 if not found.
+ */
+struct ndbtuple*
+dnsquery(char *net, char *val, char *type)
+{
+       char rip[128];
+       char *p;
+       struct ndbtuple *t;
+       int fd;
+
+       /* if the address is V4 or V6 null address, give up early */
+       if(strcmp(val, "::") == 0 || strcmp(val, "0.0.0.0") == 0)
+               return NULL;
+
+       if(net == NULL)
+               net = "/net";
+       snprintf(rip, sizeof(rip), "%s/dns", net);
+       fd = open(rip, O_RDWR);
+       if(fd < 0){
+               if(strcmp(net, "/net") == 0)
+                       snprintf(rip, sizeof(rip), "/srv/dns");
+               else {
+                       snprintf(rip, sizeof(rip), "/srv/dns%s", net);
+                       p = strrchr(rip, '/');
+                       *p = '_';
+               }
+               fd = open(rip, O_RDWR);
+               if(fd < 0)
+                       return NULL;
+#if 0
+               if(mount(fd, -1, net, MBEFORE, "") < 0){
+                       close(fd);
+                       return NULL;
+               }
+#else
+#define MBEFORE 1
+#define NOAUTHFD -1
+               int ret;
+               ret = syscall(SYS_nmount, fd, NOAUTHFD, net, MBEFORE, "");
+               if (ret < 0){
+                       close(fd);
+                       return NULL;
+               }
+#endif
+               /* fd is now closed */
+               snprintf(rip, sizeof(rip), "%s/dns", net);
+               fd = open(rip, O_RDWR);
+               if(fd < 0)
+                       return NULL;
+       }
+
+       /* zero out the error string */
+       werrstr("");
+
+       /* if this is a reverse lookup, first lookup the domain name */
+       if(strcmp(type, "ptr") == 0){
+               mkptrname(val, rip, sizeof rip);
+               t = doquery(fd, rip, "ptr");
+       } else
+               t = doquery(fd, val, type);
+
+       /*
+        * TODO: make fd static and keep it open to reduce 9P traffic
+        * walking to /net*^/dns.  Must be prepared to re-open it on error.
+        */
+       close(fd);
+       ndbsetmalloctag(t, getcallerpc(&net));
+       return t;
+}
+
+/*
+ *  convert address into a reverse lookup address
+ */
+static void
+mkptrname(char *ip, char *rip, int rlen)
+{
+       char buf[128];
+       char *p, *np;
+       int len;
+
+       if(strstr(ip, "in-addr.arpa") || strstr(ip, "IN-ADDR.ARPA")){
+               nstrcpy(rip, ip, rlen);
+               return;
+       }
+
+       nstrcpy(buf, ip, sizeof buf);
+       for(p = buf; *p; p++)
+               ;
+       *p = '.';
+       np = rip;
+       len = 0;
+       while(p >= buf){
+               len++;
+               p--;
+               if(*p == '.'){
+                       memmove(np, p+1, len);
+                       np += len;
+                       len = 0;
+               }
+       }
+       memmove(np, p+1, len);
+       np += len;
+       strcpy(np, "in-addr.arpa");
+}
+
+static void
+nstrcpy(char *to, char *from, int len)
+{
+       strncpy(to, from, len);
+       to[len-1] = 0;
+}
+
+static struct ndbtuple*
+doquery(int fd, char *dn, char *type)
+{
+       char buf[1024];
+       int n;
+       struct ndbtuple *t, *first, *last;
+
+       lseek(fd, 0, 0);
+       snprintf(buf, sizeof(buf), "!%s %s", dn, type);
+       if(write(fd, buf, strlen(buf)) < 0)
+               return NULL;
+               
+       lseek(fd, 0, 0);
+
+       first = last = NULL;
+       
+       for(;;){
+               n = read(fd, buf, sizeof(buf)-2);
+               if(n <= 0)
+                       break;
+               if(buf[n-1] != '\n')
+                       buf[n++] = '\n';        /* ndbparsline needs a trailing new line */
+               buf[n] = 0;
+
+               /* check for the error condition */
+               if(buf[0] == '!'){
+                       werrstr("%s", buf+1);
+                       return NULL;
+               }
+
+               t = _ndbparseline(buf);
+               if(t != NULL){
+                       if(first)
+                               last->entry = t;
+                       else
+                               first = t;
+                       last = t;
+
+                       while(last->entry)
+                               last = last->entry;
+               }
+       }
+
+       ndbsetmalloctag(first, getcallerpc(&fd));
+       return first;
+}
diff --git a/user/ndblib/fcallfmt.c b/user/ndblib/fcallfmt.c
new file mode 100755 (executable)
index 0000000..85c705a
--- /dev/null
@@ -0,0 +1,290 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <printf-ext.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <error.h>
+#include <libip.h>
+#include <dir.h>
+#include <ndb.h>
+#include <fcall.h>
+
+static int dumpsome(FILE *, char *, long);
+static int fdirconv(FILE *, struct dir *);
+static char *qidtype(char *, uint8_t);
+
+#define        QIDFMT  "(%.16llux %lud %s)"
+
+int printf_fcall(FILE * stream, const struct printf_info *info,
+                                const void *const *args)
+{
+       struct fcall *f = *(void **)args[0];
+       int fid, type, tag, i, retval = 0;
+       char buf[512], tmp[200];
+       char *p, *e;
+       struct dir *d;
+       struct qid *q;
+
+       e = buf + sizeof(buf);
+       type = f->type;
+       fid = f->fid;
+       tag = f->tag;
+       switch (type) {
+               case Tversion:  /* 100 */
+                       retval +=
+                               fprintf(stream, "Tversion tag %ud msize %ud version '%s'", tag,
+                                               f->msize, f->version);
+                       break;
+               case Rversion:
+                       retval +=
+                               fprintf(stream, "Rversion tag %ud msize %ud version '%s'", tag,
+                                               f->msize, f->version);
+                       break;
+               case Tauth:     /* 102 */
+                       retval +=
+                               fprintf(stream, "Tauth tag %ud afid %d uname %s aname %s", tag,
+                                               f->afid, f->uname, f->aname);
+                       break;
+               case Rauth:
+                       retval += fprintf(stream, "Rauth tag %ud qid " QIDFMT, tag,
+                                                         f->aqid.path, f->aqid.vers, qidtype(tmp,
+                                                                                                                                 f->aqid.
+                                                                                                                                 type));
+                       break;
+               case Tattach:   /* 104 */
+                       retval +=
+                               fprintf(stream,
+                                               "Tattach tag %ud fid %d afid %d uname %s aname %s", tag,
+                                               fid, f->afid, f->uname, f->aname);
+                       break;
+               case Rattach:
+                       retval += fprintf(stream, "Rattach tag %ud qid " QIDFMT, tag,
+                                                         f->qid.path, f->qid.vers, qidtype(tmp,
+                                                                                                                               f->qid.type));
+                       break;
+               case Rerror:    /* 107; 106 (Terror) illegal */
+                       retval += fprintf(stream, "Rerror tag %ud ename %s", tag, f->ename);
+                       break;
+               case Tflush:    /* 108 */
+                       retval +=
+                               fprintf(stream, "Tflush tag %ud oldtag %ud", tag, f->oldtag);
+                       break;
+               case Rflush:
+                       retval += fprintf(stream, "Rflush tag %ud", tag);
+                       break;
+               case Twalk:     /* 110 */
+                       retval +=
+                               fprintf(stream, "Twalk tag %ud fid %d newfid %d nwname %d ",
+                                               tag, fid, f->newfid, f->nwname);
+                       if (f->nwname <= MAXWELEM)
+                               for (i = 0; i < f->nwname; i++)
+                                       retval += fprintf(stream, "%d:%s ", i, f->wname[i]);
+                       break;
+               case Rwalk:
+                       retval +=
+                               fprintf(stream, "Rwalk tag %ud nwqid %ud ", tag, f->nwqid);
+                       if (f->nwqid <= MAXWELEM)
+                               for (i = 0; i < f->nwqid; i++) {
+                                       q = &f->wqid[i];
+                                       retval += fprintf(stream, "%d:" QIDFMT " ", i,
+                                                                         q->path, q->vers, qidtype(tmp, q->type));
+                               }
+                       break;
+               case Topen:     /* 112 */
+                       retval +=
+                               fprintf(stream, "Topen tag %ud fid %ud mode %d", tag, fid,
+                                               f->mode);
+                       break;
+               case Ropen:
+                       retval +=
+                               fprintf(stream, "Ropen tag %ud qid " QIDFMT " iounit %ud ", tag,
+                                               f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type),
+                                               f->iounit);
+                       break;
+               case Tcreate:   /* 114 */
+                       retval +=
+                               fprintf(stream,
+                                               "Tcreate tag %ud fid %ud name %s perm %d mode %d", tag,
+                                               fid, f->name, (uint32_t) f->perm, f->mode);
+                       break;
+               case Rcreate:
+                       retval +=
+                               fprintf(stream, "Rcreate tag %ud qid " QIDFMT " iounit %ud ",
+                                               tag, f->qid.path, f->qid.vers, qidtype(tmp,
+                                                                                                                          f->qid.type),
+                                               f->iounit);
+                       break;
+               case Tread:     /* 116 */
+                       retval +=
+                               fprintf(stream, "Tread tag %ud fid %d offset %lld count %ud",
+                                               tag, fid, f->offset, f->count);
+                       break;
+               case Rread:
+                       retval +=
+                               fprintf(stream, "Rread tag %ud count %ud ", tag, f->count);
+                       retval += dumpsome(stream, f->data, f->count);
+                       break;
+               case Twrite:    /* 118 */
+                       retval +=
+                               fprintf(stream, "Twrite tag %ud fid %d offset %lld count %ud ",
+                                               tag, fid, f->offset, f->count);
+                       retval += dumpsome(stream, f->data, f->count);
+                       break;
+               case Rwrite:
+                       retval +=
+                               fprintf(stream, "Rwrite tag %ud count %ud", tag, f->count);
+                       break;
+               case Tclunk:    /* 120 */
+                       retval += fprintf(stream, "Tclunk tag %ud fid %ud", tag, fid);
+                       break;
+               case Rclunk:
+                       retval += fprintf(stream, "Rclunk tag %ud", tag);
+                       break;
+               case Tremove:   /* 122 */
+                       retval += fprintf(stream, "Tremove tag %ud fid %ud", tag, fid);
+                       break;
+               case Rremove:
+                       retval += fprintf(stream, "Rremove tag %ud", tag);
+                       break;
+               case Tstat:     /* 124 */
+                       retval += fprintf(stream, "Tstat tag %ud fid %ud", tag, fid);
+                       break;
+               case Rstat:
+                       retval += fprintf(stream, "Rstat tag %ud ", tag);
+                       if (f->nstat > sizeof tmp)
+                               retval += fprintf(stream, " stat(%d bytes)", f->nstat);
+                       else {
+                               d = (struct dir *)tmp;
+                               convM2D(f->stat, f->nstat, d, (char *)(d + 1));
+                               retval += fprintf(stream, " stat ");
+                               retval += fdirconv(stream, d);
+                       }
+                       break;
+               case Twstat:    /* 126 */
+                       retval += fprintf(stream, "Twstat tag %ud fid %ud", tag, fid);
+                       if (f->nstat > sizeof tmp)
+                               retval += fprintf(stream, " stat(%d bytes)", f->nstat);
+                       else {
+                               d = (struct dir *)tmp;
+                               convM2D(f->stat, f->nstat, d, (char *)(d + 1));
+                               retval += fprintf(stream, " stat ");
+                               retval += fdirconv(stream, d);
+                       }
+                       break;
+               case Rwstat:
+                       retval += fprintf(stream, "Rwstat tag %ud", tag);
+                       break;
+               default:
+                       retval += fprintf(stream, "unknown type %d", type);
+       }
+       return retval;
+}
+
+static char *qidtype(char *s, uint8_t t)
+{
+       char *p;
+#define QTDIR              0x80        /* type bit for directories */
+       p = s;
+       if (t & QTDIR)
+               *p++ = 'd';
+#if 0
+       if (t & QTAPPEND)
+               *p++ = 'a';
+       if (t & QTEXCL)
+               *p++ = 'l';
+       if (t & QTAUTH)
+               *p++ = 'A';
+#endif
+       *p = '\0';
+       return s;
+}
+
+int printf_dir(FILE * stream, const struct printf_info *info,
+                          const void *const *args)
+{
+       struct dir *d = *(void **)args[0];
+       return fdirconv(stream, d);
+}
+
+static int fdirconv(FILE * stream, struct dir *d)
+{
+       char tmp[16];
+
+       return fprintf(stream, "'%s' '%s' '%s' '%s' "
+                                  "q " QIDFMT " m %#luo "
+                                  "at %ld mt %ld l %lld "
+                                  "t %d d %d",
+                                  d->name, d->uid, d->gid, d->muid,
+                                  d->qid.path, d->qid.vers, qidtype(tmp, d->qid.type), d->mode,
+                                  d->atime, d->mtime, d->length, d->type, d->dev);
+}
+
+/*
+ * dump out count (or DUMPL, if count is bigger) bytes from
+ * buf to stream, as a string if they are all printable,
+ * else as a series of hex bytes
+ */
+#define DUMPL 64
+
+static int dumpsome(FILE * stream, char *buf, long count)
+{
+       int i, printable, retval = 0;
+
+       if (buf == NULL) {
+               return fprintf(stream, "<no data>");
+       }
+       printable = 1;
+       if (count > DUMPL)
+               count = DUMPL;
+       for (i = 0; i < count && printable; i++)
+               if ((buf[i] < 32 && buf[i] != '\n' && buf[i] != '\t')
+                       || (uint8_t) buf[i] > 127)
+                       printable = 0;
+       retval += fprintf(stream, "'");
+       if (printable) {
+               retval += fprintf(stream, "%s", buf);
+       } else {
+               for (i = 0; i < count; i++) {
+                       if (i > 0 && i % 4 == 0)
+                               retval += fprintf(stream, " ");
+                       retval += fprintf(stream, "%2.2ux", buf[i]);
+               }
+       }
+       retval += fprintf(stream, "'");
+       return retval;
+}
+
+int printf_fcall_info(const struct printf_info *info, size_t n, int *argtypes,
+                                         int *size)
+{
+       if (n > 0) {
+               argtypes[0] = PA_POINTER;
+               size[0] = sizeof(uint8_t *);
+       }
+       return 1;
+}
+
+int printf_dir_info(const struct printf_info *info, size_t n, int *argtypes,
+                                       int *size)
+{
+       if (n > 0) {
+               argtypes[0] = PA_POINTER;
+               size[0] = sizeof(uint8_t *);
+       }
+       return 1;
+}
diff --git a/user/ndblib/include/dir.h b/user/ndblib/include/dir.h
new file mode 100755 (executable)
index 0000000..7d8b37e
--- /dev/null
@@ -0,0 +1,40 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#ifndef ROS_INC_DIR_H
+
+#define ROS_INC_DIR_H
+
+/* STATFIXLEN includes leading 16-bit count */
+/* The count, however, excludes itself; total size is BIT16SZ+count */
+#define STATFIXLEN     (BIT16SZ+QIDSZ+5*BIT16SZ+4*BIT32SZ+1*BIT64SZ)   /* amount of fixed length data in a stat buffer */
+
+struct qid
+{
+       uint64_t        path;
+       uint32_t        vers;
+       uint8_t type;
+};
+
+struct dir {
+       /* system-modified data */
+       uint16_t        type;   /* server type */
+       unsigned int    dev;    /* server subtype */
+       /* file data */
+       struct qid      qid;    /* unique id from server */
+       uint32_t        mode;   /* permissions */
+       uint32_t        atime;  /* last read time */
+       uint32_t        mtime;  /* last write time */
+       int64_t length; /* file length: see <u.h> */
+       char    *name;  /* last element of path */
+       char    *uid;   /* owner name */
+       char    *gid;   /* group name */
+       char    *muid;  /* last modifier name */
+};
+
+#endif /* ROS_INC_DIR_H */
diff --git a/user/ndblib/include/fcall.h b/user/ndblib/include/fcall.h
new file mode 100755 (executable)
index 0000000..2cf49a4
--- /dev/null
@@ -0,0 +1,155 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+/*
+
+ * Copyright 2013 Google Inc.
+ * Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories.
+ */
+
+#ifndef ROS_INC_FCALL_H
+#define ROS_INC_FCALL_H
+
+#include <printf-ext.h>
+
+#define        VERSION9P       "9P2000"
+
+#define        MAXWELEM        16
+
+       struct fcall {
+       uint8_t type;
+       uint32_t fid;
+       uint16_t tag;
+       union {
+               struct {
+                       uint32_t msize;         /* Tversion, Rversion */
+                       char *version;          /* Tversion, Rversion */
+               };
+               struct {
+                       uint16_t oldtag;        /* Tflush */
+               };
+               struct {
+                       char *ename;            /* Rerror */
+               };
+               struct {
+                       struct qid qid;         /* Rattach, Ropen, Rcreate */
+                       uint32_t iounit;        /* Ropen, Rcreate */
+               };
+               struct {
+                       struct qid aqid;        /* Rauth */
+               };
+               struct {
+                       uint32_t afid;          /* Tauth, Tattach */
+                       char *uname;            /* Tauth, Tattach */
+                       char *aname;            /* Tauth, Tattach */
+               };
+               struct {
+                       uint32_t perm;          /* Tcreate */
+                       char *name;                     /* Tcreate */
+                       uint8_t mode;           /* Tcreate, Topen */
+               };
+               struct {
+                       uint32_t newfid;        /* Twalk */
+                       uint16_t nwname;        /* Twalk */
+                       char *wname[MAXWELEM];  /* Twalk */
+               };
+               struct {
+                       uint16_t nwqid;         /* Rwalk */
+                       struct qid wqid[MAXWELEM];      /* Rwalk */
+               };
+               struct {
+                       int64_t offset;         /* Tread, Twrite */
+                       uint32_t count;         /* Tread, Twrite, Rread */
+                       char *data;                     /* Twrite, Rread */
+               };
+               struct {
+                       uint16_t nstat;         /* Twstat, Rstat */
+                       uint8_t *stat;          /* Twstat, Rstat */
+               };
+       };
+} fcall;
+
+#define        GBIT8(p)        ((p)[0])
+#define        GBIT16(p)       ((p)[0]|((p)[1]<<8))
+#define        GBIT32(p)       ((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24))
+#define        GBIT64(p)       ((uint32_t)((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24)) |\
+                               ((int64_t)((p)[4]|((p)[5]<<8)|((p)[6]<<16)|((p)[7]<<24)) << 32))
+
+#define        PBIT8(p,v)      (p)[0]=(v)
+#define        PBIT16(p,v)     (p)[0]=(v);(p)[1]=(v)>>8
+#define        PBIT32(p,v)     (p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24
+#define        PBIT64(p,v)     (p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24;\
+                       (p)[4]=(v)>>32;(p)[5]=(v)>>40;(p)[6]=(v)>>48;(p)[7]=(v)>>56
+
+#define        BIT8SZ          1
+#define        BIT16SZ         2
+#define        BIT32SZ         4
+#define        BIT64SZ         8
+#define        QIDSZ   (BIT8SZ+BIT32SZ+BIT64SZ)
+
+/* STATFIXLEN includes leading 16-bit count */
+/* The count, however, excludes itself; total size is BIT16SZ+count */
+#define STATFIXLEN     (BIT16SZ+QIDSZ+5*BIT16SZ+4*BIT32SZ+1*BIT64SZ)   /* amount of fixed length data in a stat buffer */
+
+#define        NOTAG           (uint16_t)~0U   /* Dummy tag */
+#define        NOFID           (uint32_t)~0U   /* Dummy fid */
+#define        IOHDRSZ         24      /* ample room for Twrite/Rread header (iounit) */
+
+enum {
+       Tversion = 100,
+       Rversion,
+       Tauth = 102,
+       Rauth,
+       Tattach = 104,
+       Rattach,
+       Terror = 106,   /* illegal */
+       Rerror,
+       Tflush = 108,
+       Rflush,
+       Twalk = 110,
+       Rwalk,
+       Topen = 112,
+       Ropen,
+       Tcreate = 114,
+       Rcreate,
+       Tread = 116,
+       Rread,
+       Twrite = 118,
+       Rwrite,
+       Tclunk = 120,
+       Rclunk,
+       Tremove = 122,
+       Rremove,
+       Tstat = 124,
+       Rstat,
+       Twstat = 126,
+       Rwstat,
+       Tmax,
+};
+
+unsigned int convM2S(uint8_t *, unsigned int, struct fcall *);
+unsigned int convS2M(struct fcall *, uint8_t *, unsigned int);
+unsigned int sizeS2M(struct fcall *);
+
+int statcheck(uint8_t * abuf, unsigned int nbuf);
+unsigned int convM2D(uint8_t *, unsigned int, struct dir *, char *);
+unsigned int convD2M(struct dir *, uint8_t *, unsigned int);
+unsigned int sizeD2M(struct dir *);
+
+int printf_fcall(FILE *stream, const struct printf_info *info,
+                 const void *const *args);
+int printf_fcall_info(const struct printf_info* info, size_t n, int *argtypes,
+                      int *size);
+int printf_dir(FILE *stream, const struct printf_info *info,
+               const void *const *args);
+int printf_dir_info(const struct printf_info* info, size_t n, int *argtypes,
+                    int *size);
+
+int read9pmsg(int, void *, unsigned int);
+
+#endif /* ROS_INC_FCALL_H */
diff --git a/user/ndblib/include/ndb.h b/user/ndblib/include/ndb.h
new file mode 100755 (executable)
index 0000000..65d5ab8
--- /dev/null
@@ -0,0 +1,181 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#ifndef ROS_INC_NDB_H
+
+#define ROS_INC_NDB_H
+
+#include <dir.h>
+
+enum
+{
+       Ndbalen=        32,     /* max attribute length */
+       Ndbvlen=        64,     /* max value length */
+};
+
+struct ndbcache;
+/*
+ *  the database
+ */
+struct ndb
+{
+       struct ndb              *next;
+
+       FILE    *b;             /* buffered input file */
+       uint8_t         buf[256];       /* and its buffer */
+
+       uint32_t                mtime;          /* mtime of db file */
+       struct qid              qid;            /* qid of db file */
+       char            file[128];/* path name of db file */
+       uint32_t                length;         /* length of db file */
+       int             isopen; /* true if the file is open */
+
+       int             nohash;         /* don't look for hash files */
+       struct ndbhf            *hf;            /* open hash files */
+
+       int             ncache;         /* size of tuple cache */
+       struct ndbcache *cache;         /* cached entries */
+};
+
+/*
+ *  a parsed entry, doubly linked
+ */
+struct ndbtuple
+{
+       char            attr[Ndbalen];          /* attribute name */
+       char            *val;                   /* value(s) */
+       struct ndbtuple *entry;                 /* next tuple in this entry */
+       struct ndbtuple *line;                  /* next tuple on this line */
+       uint32_t                ptr;                    /* (for the application - starts 0) */
+       char            valbuf[Ndbvlen];        /* initial allocation for value */
+};
+
+/*
+ *  each hash file is of the form
+ *
+ *             +---------------------------------------+
+ *             |       mtime of db file (4 bytes)      |
+ *             +---------------------------------------+
+ *             |  size of table (in entries - 4 bytes) |
+ *             +---------------------------------------+
+ *             |               hash table              |
+ *             +---------------------------------------+
+ *             |               hash chains             |
+ *             +---------------------------------------+
+ *
+ *  hash collisions are resolved using chained entries added to the
+ *  the end of the hash table.
+ *
+ *  Hash entries are of the form
+ *
+ *             +-------------------------------+
+ *             |       offset  (3 bytes)       |
+ *             +-------------------------------+
+ *
+ *  Chain entries are of the form
+ *
+ *             +-------------------------------+
+ *             |       offset1 (3 bytes)       |
+ *             +-------------------------------+
+ *             |       offset2 (3 bytes)       |
+ *             +-------------------------------+
+ *
+ *  The top bit of an offset set to 1 indicates a pointer to a hash chain entry.
+ */
+#define NDBULLEN       4               /* unsigned long length in bytes */
+#define NDBPLEN                3               /* pointer length in bytes */
+#define NDBHLEN                (2*NDBULLEN)    /* hash file header length in bytes */
+
+/*
+ *  finger pointing to current point in a search
+ */
+struct ndbs
+{
+       struct ndb      *db;    /* data base file being searched */
+       struct ndbhf    *hf;    /* hash file being searched */
+       int     type;
+       uint32_t        ptr;    /* current pointer */
+       uint32_t        ptr1;   /* next pointer */
+       struct ndbtuple *t;     /* last attribute value pair found */
+};
+
+struct ndbcache
+{
+       struct ndbcache *next;
+       char            *attr;
+       char            *val;
+       struct ndbs             s;
+       struct ndbtuple *t;
+};
+
+/*
+ *  bit defs for pointers in hash files
+ */
+#define NDBSPEC        (1<<23)
+#define NDBCHAIN       NDBSPEC         /* points to a collision chain */
+#define NDBNAP         (NDBSPEC|1)     /* not a pointer */
+
+/*
+ *  macros for packing and unpacking pointers
+ */
+#define NDBPUTP(v,a) { (a)[0] = v; (a)[1] = (v)>>8; (a)[2] = (v)>>16; }
+#define NDBGETP(a) ((a)[0] | ((a)[1]<<8) | ((a)[2]<<16))
+
+/*
+ *  macros for packing and unpacking unsigned longs
+ */
+#define NDBPUTUL(v,a) { (a)[0] = v; (a)[1] = (v)>>8; (a)[2] = (v)>>16; (a)[3] = (v)>>24; }
+#define NDBGETUL(a) ((a)[0] | ((a)[1]<<8) | ((a)[2]<<16) | ((a)[3]<<24))
+
+#define NDB_IPlen 16
+
+struct ndbtuple*       csgetval(char*, char*, char*, char*, char*);
+char*          csgetvalue(char*, char*, char*, char*, struct ndbtuple**);
+struct ndbtuple*       csipinfo(char*, char*, char*, char**, int);
+struct ndbtuple*       dnsquery(char*, char*, char*);
+char*          ipattr(char*);
+struct ndb*            ndbcat(struct ndb*, struct ndb*);
+int            ndbchanged(struct ndb*);
+void           ndbclose(struct ndb*);
+struct ndbtuple*       ndbconcatenate(struct ndbtuple*, struct ndbtuple*);
+struct ndbtuple*       ndbdiscard(struct ndbtuple*, struct ndbtuple*);
+void           ndbfree(struct ndbtuple*);
+struct ndbtuple*       ndbgetipaddr(struct ndb*, char*);
+struct ndbtuple*       ndbgetval(struct ndb*,
+                                 struct ndbs*, char*, char*, char*, char*);
+char*          ndbgetvalue(struct ndb*, struct ndbs*, char*, char*, char*,
+                                struct ndbtuple**);
+struct ndbtuple*       ndbfindattr(struct ndbtuple*, struct ndbtuple*, char*);
+uint32_t               ndbhash(char*, int);
+struct ndbtuple*       ndbipinfo(struct ndb*, char*, char*, char**, int);
+struct ndbtuple*       ndblookval(struct ndbtuple*,
+                                  struct ndbtuple*, char*, char*);
+struct ndbtuple*       ndbnew(char*, char*);
+struct ndb*            ndbopen(char*);
+struct ndbtuple*       ndbparse(struct ndb*);
+int            ndbreopen(struct ndb*);
+struct ndbtuple*       ndbreorder(struct ndbtuple*, struct ndbtuple*);
+struct ndbtuple*       ndbsearch(struct ndb*, struct ndbs*, char*, char*);
+void           ndbsetval(struct ndbtuple*, char*, int);
+struct ndbtuple*       ndbsnext(struct ndbs*, char*, char*);
+struct ndbtuple*       ndbsubstitute(struct ndbtuple*, struct ndbtuple*,
+                                     struct ndbtuple*);
+char*_ndbparsetuple(char *cp, struct ndbtuple **tp);
+struct ndbtuple*_ndbparseline(char *cp);
+//void         ndbsetmalloctag(struct ndbtuple*, uintptr_t);
+static inline void             ndbsetmalloctag(struct ndbtuple*t, uintptr_t v){}
+
+static inline void werrstr(char *v, ...){}
+static inline uintptr_t getcallerpc(void *v){return 0;}
+static inline void setmalloctag(void *v){}
+
+void _ndbcacheflush(struct ndb *db);
+/* No implementation for this, dumped into a garbage file */
+void setnetmtpt(char *net, int n, char *x);
+
+#endif /* ROS_INC_NDB_H */
diff --git a/user/ndblib/ipattr.c b/user/ndblib/ipattr.c
new file mode 100755 (executable)
index 0000000..e71a68a
--- /dev/null
@@ -0,0 +1,62 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <libip.h>
+#include <ndb.h>
+
+/*
+ *  return ndb attribute type of an ip name
+ */
+char*
+ipattr(char *name)
+{
+       char *p, c;
+       int dot = 0;
+       int alpha = 0;
+       int colon = 0;
+       int hex = 0;
+
+       for(p = name; *p; p++){
+               c = *p;
+               if(isdigit(c))
+                       continue;
+               if(isxdigit(c))
+                       hex = 1;
+               else if(isalpha(c) || c == '-')
+                       alpha = 1;
+               else if(c == '.')
+                       dot = 1;
+               else if(c == ':')
+                       colon = 1;
+               else
+                       return "sys";
+       }
+
+       if(alpha){
+               if(dot)
+                       return "dom";
+               else
+                       return "sys";
+       }
+
+       if(colon)
+               return "ip";    /* ip v6 */
+
+       if(dot && !hex)
+               return "ip";
+       else
+               return "sys";
+}
diff --git a/user/ndblib/ndbaux.c b/user/ndblib/ndbaux.c
new file mode 100755 (executable)
index 0000000..7903110
--- /dev/null
@@ -0,0 +1,109 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <libip.h>
+#include <ndb.h>
+
+
+/*
+ *  parse a single tuple
+ */
+char*
+_ndbparsetuple(char *cp, struct ndbtuple **tp)
+{
+       char *p;
+       int len;
+       struct ndbtuple *t;
+
+       /* a '#' starts a comment lasting till new line */
+       while (isspace(*cp) && *cp)
+               cp++;
+       if (!*cp)
+               return 0;
+       if(*cp == '#' || *cp == '\n')
+               return 0;
+
+       t = ndbnew(NULL, NULL);
+       *tp = t;
+
+       /* parse attribute */
+       p = cp;
+       while(*cp != '=' && !isspace(*cp) && *cp != '\n')
+               cp++;
+       len = cp - p;
+       if(len >= Ndbalen)
+               len = Ndbalen-1;
+       strncpy(t->attr, p, len);
+
+       /* parse value */
+       while (isspace(*cp) && *cp)
+               cp++;
+       if(*cp == '='){
+               cp++;
+               if(*cp == '"'){
+                       p = ++cp;
+                       while(*cp != '\n' && *cp != '"')
+                               cp++;
+                       len = cp - p;
+                       if(*cp == '"')
+                               cp++;
+               } else if(*cp == '#'){
+                       len = 0;
+               } else {
+                       p = cp;
+                       while(!isspace(*cp) && *cp != '\n')
+                               cp++;
+                       len = cp - p;
+               }
+               ndbsetval(t, p, len);
+       }
+
+       return cp;
+}
+
+/*
+ *  parse all tuples in a line.  we assume that the 
+ *  line ends in a '\n'.
+ *
+ *  the tuples are linked as a list using ->entry and
+ *  as a ring using ->line.
+ */
+struct ndbtuple*
+_ndbparseline(char *cp)
+{
+       struct ndbtuple *t;
+       struct ndbtuple *first, *last;
+
+       first = last = 0;
+       while(*cp != '#' && *cp != '\n'){
+               t = 0;
+               cp = _ndbparsetuple(cp, &t);
+               if(cp == 0)
+                       break;
+               if(first){
+                       last->line = t;
+                       last->entry = t;
+               } else
+                       first = t;
+               last = t;
+               t->line = 0;
+               t->entry = 0;
+       }
+       if(first)
+               last->line = first;
+       ndbsetmalloctag(first, getcallerpc(&cp));
+       return first;
+}
diff --git a/user/ndblib/ndbcache.c b/user/ndblib/ndbcache.c
new file mode 100755 (executable)
index 0000000..12b6ea9
--- /dev/null
@@ -0,0 +1,153 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <libip.h>
+#include <ndb.h>
+
+enum
+{
+       Maxcached=      128,
+};
+
+static void
+ndbcachefree(struct ndbcache *c)
+{
+       free(c->val);
+       free(c->attr);
+       if(c->t)
+               ndbfree(c->t);
+       free(c);
+}
+
+static struct ndbtuple*
+ndbcopy(struct ndb *db, struct ndbtuple *from_t, struct ndbs *from_s,
+       struct ndbs *to_s)
+{
+       struct ndbtuple *first, *to_t, *last, *line;
+       int newline;
+
+       *to_s = *from_s;
+       to_s->t = NULL;
+       to_s->db = db;
+
+       newline = 1;
+       last = NULL;
+       first = NULL;
+       line = NULL;
+       for(; from_t != NULL; from_t = from_t->entry){
+               to_t = ndbnew(from_t->attr, from_t->val);
+
+               /* have s point to matching tuple */
+               if(from_s->t == from_t)
+                       to_s->t = to_t;
+
+               if(newline)
+                       line = to_t;
+               else
+                       last->line = to_t;
+
+               if(last != NULL)
+                       last->entry = to_t;
+               else {
+                       first = to_t;
+                       line = to_t;
+               }
+               to_t->entry = NULL;
+               to_t->line = line;
+               last = to_t;
+               newline = from_t->line != from_t->entry;
+       }
+       ndbsetmalloctag(first, getcallerpc(&db));
+       return first;
+}
+
+/*
+ *  if found, move to front
+ */
+int
+_ndbcachesearch(struct ndb *db,
+               struct ndbs *s, char *attr, char *val, struct ndbtuple **t)
+{
+       struct ndbcache *c, **l;
+
+       *t = NULL;
+       c = NULL;
+       for(l = &db->cache; *l != NULL; l = &(*l)->next){
+               c = *l;
+               if(strcmp(c->attr, attr) == 0 && strcmp(c->val, val) == 0)
+                       break;
+       }
+       if(*l == NULL)
+               return -1;
+
+       /* move to front */
+       *l = c->next;
+       c->next = db->cache;
+       db->cache = c;
+
+       *t = ndbcopy(db, c->t, &c->s, s);
+       return 0;
+}
+
+struct ndbtuple*
+_ndbcacheadd(struct ndb *db,
+            struct ndbs *s, char *attr, char *val, struct ndbtuple *t)
+{
+       struct ndbcache *c, **l;
+
+       c = calloc(1, sizeof *c);
+       if(c == NULL)
+               return NULL;
+       c->attr = strdup(attr);
+       if(c->attr == NULL)
+               goto err;
+       c->val = strdup(val);
+       if(c->val == NULL)
+               goto err;
+       c->t = ndbcopy(db, t, s, &c->s);
+       if(c->t == NULL && t != NULL)
+               goto err;
+
+       /* add to front */
+       c->next = db->cache;
+       db->cache = c;
+
+       /* trim list */
+       if(db->ncache < Maxcached){
+               db->ncache++;
+               return t;
+       }
+       for(l = &db->cache; (*l)->next; l = &(*l)->next)
+               ;
+       c = *l;
+       *l = NULL;
+err:
+       ndbcachefree(c);
+       ndbsetmalloctag(t, getcallerpc(&db));
+       return t;
+}
+
+void
+_ndbcacheflush(struct ndb *db)
+{
+       struct ndbcache *c;
+
+       while(db->cache != NULL){
+               c = db->cache;
+               db->cache = c->next;
+               ndbcachefree(c);
+       }
+       db->ncache = 0;
+}
diff --git a/user/ndblib/ndbcat.c b/user/ndblib/ndbcat.c
new file mode 100755 (executable)
index 0000000..f9c35df
--- /dev/null
@@ -0,0 +1,30 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <libip.h>
+#include <ndb.h>
+
+struct ndb*
+ndbcat(struct ndb *a, struct ndb *b)
+{
+       struct ndb *db = a;
+
+       if(a == NULL)
+               return b;
+       while(a->next != NULL)
+               a = a->next;
+       a->next = b;
+       return db;
+}
diff --git a/user/ndblib/ndbconcatenate.c b/user/ndblib/ndbconcatenate.c
new file mode 100755 (executable)
index 0000000..daec8e0
--- /dev/null
@@ -0,0 +1,32 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <libip.h>
+#include <ndb.h>
+
+/* concatenate two tuples */
+struct ndbtuple*
+ndbconcatenate(struct ndbtuple *a, struct ndbtuple *b)
+{
+       struct ndbtuple *t;
+
+       if(a == NULL)
+               return b;
+       for(t = a; t->entry; t = t->entry)
+               ;
+       t->entry = b;
+       ndbsetmalloctag(a, getcallerpc(&a));
+       return a;
+}
diff --git a/user/ndblib/ndbdiscard.c b/user/ndblib/ndbdiscard.c
new file mode 100755 (executable)
index 0000000..1249244
--- /dev/null
@@ -0,0 +1,43 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <libip.h>
+#include <ndb.h>
+
+/* remove a from t and free it */
+struct ndbtuple*
+ndbdiscard(struct ndbtuple *t, struct ndbtuple *a)
+{
+       struct ndbtuple *nt;
+
+       /* unchain a */
+       for(nt = t; nt != NULL; nt = nt->entry){
+               if(nt->line == a)
+                       nt->line = a->line;
+               if(nt->entry == a)
+                       nt->entry = a->entry;
+       }
+
+       /* a may be start of chain */
+       if(t == a)
+               t = a->entry;
+
+       /* free a */
+       a->entry = NULL;
+       ndbfree(a);
+
+       ndbsetmalloctag(t, getcallerpc(&t));
+       return t;
+}
diff --git a/user/ndblib/ndbfree.c b/user/ndblib/ndbfree.c
new file mode 100755 (executable)
index 0000000..33fcbd2
--- /dev/null
@@ -0,0 +1,93 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <libip.h>
+#include <ndb.h>
+
+/*
+ *  free a parsed entry
+ */
+void
+ndbfree(struct ndbtuple *t)
+{
+       struct ndbtuple *tn;
+
+       for(; t; t = tn){
+               tn = t->entry;
+               if(t->val != t->valbuf){
+                       free(t->val);
+               }
+               free(t);
+       }
+}
+
+/*
+ *  set a value in a tuple
+ */
+void
+ndbsetval(struct ndbtuple *t, char *val, int n)
+{
+       if(n < Ndbvlen){
+               if(t->val != t->valbuf){
+                       free(t->val);
+                       t->val = t->valbuf;
+               }
+       } else {
+               if(t->val != t->valbuf)
+                       t->val = realloc(t->val, n+1);
+               else
+                       t->val = calloc(n + 1, 1);
+               if(t->val == NULL){
+                       fprintf(stderr, "ndbsetval %r");
+                       exit(1);
+               }
+       }
+       strncpy(t->val, val, n);
+       t->val[n] = 0;
+}
+
+/*
+ *  allocate a tuple
+ */
+struct ndbtuple*
+ndbnew(char *attr, char *val)
+{
+       struct ndbtuple *t;
+
+       t = calloc(1, sizeof(*t));
+       if(t == NULL){
+               fprintf(stderr, "ndbnew %r");
+               exit(1);
+       }
+       if(attr != NULL)
+               strncpy(t->attr, attr, sizeof(t->attr)-1);
+       t->val = t->valbuf;
+       if(val != NULL)
+               ndbsetval(t, val, strlen(val));
+       ndbsetmalloctag(t, getcallerpc(&attr));
+       return t;       
+}
+
+#if 0
+/*
+ *  set owner of a tuple
+ */
+void
+ndbsetmalloctag(struct ndbtuple *t, uintptr_t tag)
+{
+       for(; t; t=t->entry)
+               {}
+}
+#endif
diff --git a/user/ndblib/ndbgetipaddr.c b/user/ndblib/ndbgetipaddr.c
new file mode 100755 (executable)
index 0000000..7ebf364
--- /dev/null
@@ -0,0 +1,61 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <libip.h>
+#include <ndb.h>
+
+/* return list of ip addresses for a name */
+struct ndbtuple*
+ndbgetipaddr(struct ndb *db, char *val)
+{
+       char *attr, *p;
+       struct ndbtuple *it, *first, *last, *next;
+       struct ndbs s;
+
+       /* already an IP address? */
+       attr = ipattr(val);
+       if(strcmp(attr, "ip") == 0){
+               it = ndbnew("ip", val);
+               ndbsetmalloctag(it, getcallerpc(&db));
+               return it;
+       }
+
+       /* look it up */
+       p = ndbgetvalue(db, &s, attr, val, "ip", &it);
+       if(p == NULL)
+               return NULL;
+       free(p);
+
+       /* remove the non-ip entries */
+       first = last = NULL;
+       for(; it; it = next){
+               next = it->entry;
+               if(strcmp(it->attr, "ip") == 0){
+                       if(first == NULL)
+                               first = it;
+                       else
+                               last->entry = it;
+                       it->entry = NULL;
+                       it->line = first;
+                       last = it;
+               } else {
+                       it->entry = NULL;
+                       ndbfree(it);
+               }
+       }
+
+       ndbsetmalloctag(first, getcallerpc(&db));
+       return first;
+}
diff --git a/user/ndblib/ndbgetval.c b/user/ndblib/ndbgetval.c
new file mode 100755 (executable)
index 0000000..e45f56c
--- /dev/null
@@ -0,0 +1,91 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <libip.h>
+#include <ndb.h>
+
+/*
+ *  search for a tuple that has the given 'attr=val' and also 'rattr=x'.
+ *  copy 'x' into 'buf' and return the whole tuple.
+ *
+ *  return 0 if not found.
+ */
+char*
+ndbgetvalue(struct ndb *db, struct ndbs *s, char *attr, char *val, char *rattr,
+           struct ndbtuple **pp)
+{
+       struct ndbtuple *t, *nt;
+       char *rv;
+       struct ndbs temps;
+
+       if(s == NULL)
+               s = &temps;
+       if(pp)
+               *pp = NULL;
+       t = ndbsearch(db, s, attr, val);
+       while(t){
+               /* first look on same line (closer binding) */
+               nt = s->t;
+               for(;;){
+                       if(strcmp(rattr, nt->attr) == 0){
+                               rv = strdup(nt->val);
+                               if(pp != NULL)
+                                       *pp = t;
+                               else
+                                       ndbfree(t);
+                               return rv;
+                       }
+                       nt = nt->line;
+                       if(nt == s->t)
+                               break;
+               }
+               /* search whole tuple */
+               for(nt = t; nt; nt = nt->entry){
+                       if(strcmp(rattr, nt->attr) == 0){
+                               rv = strdup(nt->val);
+                               if(pp != NULL)
+                                       *pp = t;
+                               else
+                                       ndbfree(t);
+                               return rv;
+                       }
+               }
+               ndbfree(t);
+               t = ndbsnext(s, attr, val);
+       }
+       return NULL;
+}
+
+struct ndbtuple*
+ndbgetval(struct ndb *db,
+         struct ndbs *s, char *attr, char *val, char *rattr, char *buf)
+{
+       struct ndbtuple *t;
+       char *p;
+
+       p = ndbgetvalue(db, s, attr, val, rattr, &t);
+       if(p == NULL){
+               if(buf != NULL)
+                       *buf = 0;
+       } else {
+               if(buf != NULL){
+                       strncpy(buf, p, Ndbvlen-1);
+                       buf[Ndbvlen-1] = 0;
+               }
+               free(p);
+       }
+       ndbsetmalloctag(t, getcallerpc(&db));
+       return t;
+}
diff --git a/user/ndblib/ndbhash.c b/user/ndblib/ndbhash.c
new file mode 100755 (executable)
index 0000000..7654e41
--- /dev/null
@@ -0,0 +1,277 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <libip.h>
+#include <ndb.h>
+#include <ndbhf.h>
+
+enum {
+       Dptr,   /* pointer to database file */
+       Cptr,   /* pointer to first chain entry */
+       Cptr1,  /* pointer to second chain entry */
+};
+
+/*
+ *  generate a hash value for an ascii string (val) given
+ *  a hash table length (hlen)
+ */
+uint32_t
+ndbhash(char *vp, int hlen)
+{
+       uint32_t hash;
+       uint8_t *val = (uint8_t*)vp;
+
+       for(hash = 0; *val; val++)
+               hash = (hash*13) + *val-'a';
+       return hash % hlen;
+}
+
+/*
+ *  read a hash file with buffering
+ */
+static uint8_t*
+hfread(struct ndbhf *hf, long off, int len)
+{
+       if(off < hf->off || off + len > hf->off + hf->len){
+               if(lseek(hf->fd, off, 0) < 0
+               || (hf->len = read(hf->fd, hf->buf, sizeof(hf->buf))) < len){
+                       hf->off = -1;
+                       return 0;
+               }
+               hf->off = off;
+       }
+       return &hf->buf[off-hf->off];
+}
+
+/*
+ *  return an opened hash file if one exists for the
+ *  attribute and if it is current vis-a-vis the data
+ *  base file
+ */
+static struct ndbhf*
+hfopen(struct ndb *db, char *attr)
+{
+       struct ndbhf *hf;
+       char buf[sizeof(hf->attr)+sizeof(db->file)+2];
+       uint8_t *p;
+       struct dir *d;
+
+       /* try opening the data base if it's closed */
+       if(db->mtime==0 && ndbreopen(db) < 0)
+               return 0;
+
+       /* if the database has changed, throw out hash files and reopen db */
+#if 0
+       if((d = dirfstat(Bfildes(&db->b))) == NULL || db->qid.path != d->qid.path
+       || db->qid.vers != d->qid.vers){
+#else
+       if (1){
+#endif
+               if(ndbreopen(db) < 0){
+                       free(d);
+                       return 0;
+               }
+       }
+       free(d);
+
+       if(db->nohash)
+               return 0;
+
+       /* see if a hash file exists for this attribute */
+       for(hf = db->hf; hf; hf= hf->next){
+               if(strcmp(hf->attr, attr) == 0)
+                       return hf;
+       }
+
+       /* create a new one */
+       hf = (struct ndbhf*)calloc(sizeof(struct ndbhf), 1);
+       if(hf == 0)
+               return 0;
+       memset(hf, 0, sizeof(struct ndbhf));
+
+       /* compare it to the database file */
+       strncpy(hf->attr, attr, sizeof(hf->attr)-1);
+       sprintf(buf, "%s.%s", db->file, hf->attr);
+       hf->fd = open(buf, O_RDONLY);
+       if(hf->fd >= 0){
+               hf->len = 0;
+               hf->off = 0;
+               p = hfread(hf, 0, 2*NDBULLEN);
+               if(p){
+                       hf->dbmtime = NDBGETUL(p);
+                       hf->hlen = NDBGETUL(p+NDBULLEN);
+                       if(hf->dbmtime == db->mtime){
+                               hf->next = db->hf;
+                               db->hf = hf;
+                               return hf;
+                       }
+               }
+               close(hf->fd);
+       }
+
+       free(hf);
+       return 0;
+}
+
+/*
+ *  return the first matching entry
+ */
+struct ndbtuple*
+ndbsearch(struct ndb *db, struct ndbs *s, char *attr, char *val)
+{
+       uint8_t *p;
+       struct ndbtuple *t;
+       struct ndbhf *hf;
+
+       hf = hfopen(db, attr);
+
+       memset(s, 0, sizeof(*s));
+       if(_ndbcachesearch(db, s, attr, val, &t) == 0){
+               /* found in cache */
+               if(t != NULL){
+                       ndbsetmalloctag(t, getcallerpc(&db));
+                       return t;       /* answer from this file */
+               }
+               if(db->next == NULL)
+                       return NULL;
+               t = ndbsearch(db->next, s, attr, val);
+               ndbsetmalloctag(t, getcallerpc(&db));
+               return t;
+       }
+
+       s->db = db;
+       s->hf = hf;
+       if(s->hf){
+               s->ptr = ndbhash(val, s->hf->hlen)*NDBPLEN;
+               p = hfread(s->hf, s->ptr+NDBHLEN, NDBPLEN);
+               if(p == 0){
+                       t = _ndbcacheadd(db, s, attr, val, NULL);
+                       ndbsetmalloctag(t, getcallerpc(&db));
+                       return t;
+               }
+               s->ptr = NDBGETP(p);
+               s->type = Cptr1;
+       } else if(db->length > 128*1024){
+               printf("Missing or out of date hash file %s.%s.\n", db->file, attr);
+               //syslog(0, "ndb", "Missing or out of date hash file %s.%s.", db->file, attr);
+
+               /* advance search to next db file */
+               s->ptr = NDBNAP;
+               _ndbcacheadd(db, s, attr, val, NULL);
+               if(db->next == 0)
+                       return NULL;
+               t = ndbsearch(db->next, s, attr, val);
+               ndbsetmalloctag(t, getcallerpc(&db));
+               return t;
+       } else {
+               s->ptr = 0;
+               s->type = Dptr;
+       }
+       t = ndbsnext(s, attr, val);
+       _ndbcacheadd(db, s, attr, val, (t != NULL && s->db == db)?t:NULL);
+       ndbsetmalloctag(t, getcallerpc(&db));
+       return t;
+}
+
+static struct ndbtuple*
+match(struct ndbtuple *t, char *attr, char *val)
+{
+       struct ndbtuple *nt;
+
+       for(nt = t; nt; nt = nt->entry)
+               if(strcmp(attr, nt->attr) == 0
+               && strcmp(val, nt->val) == 0)
+                       return nt;
+       return 0;
+}
+
+/*
+ *  return the next matching entry in the hash chain
+ */
+struct ndbtuple*
+ndbsnext(struct ndbs *s, char *attr, char *val)
+{
+       struct ndbtuple *t;
+       struct ndb *db;
+       uint8_t *p;
+
+       db = s->db;
+       if(s->ptr == NDBNAP)
+               goto nextfile;
+
+       for(;;){
+               if(s->type == Dptr){
+                       if(fseek(db->b, s->ptr, 0) < 0)
+                               break;
+                       t = ndbparse(db);
+                       s->ptr = ftell(db->b);
+                       if(t == 0)
+                               break;
+                       if(s->t = match(t, attr, val)){
+                               ndbsetmalloctag(t, getcallerpc(&s));
+                               return t;
+                       }
+                       ndbfree(t);
+               } else if(s->type == Cptr){
+                       if(fseek(db->b, s->ptr, 0) < 0)
+                               break; 
+                       s->ptr = s->ptr1;
+                       s->type = Cptr1;
+                       t = ndbparse(db);
+                       if(t == 0)
+                               break;
+                       if(s->t = match(t, attr, val)){
+                               ndbsetmalloctag(t, getcallerpc(&s));
+                               return t;
+                       }
+                       ndbfree(t);
+               } else if(s->type == Cptr1){
+                       if(s->ptr & NDBCHAIN){  /* hash chain continuation */
+                               s->ptr &= ~NDBCHAIN;
+                               p = hfread(s->hf, s->ptr+NDBHLEN, 2*NDBPLEN);
+                               if(p == 0)
+                                       break;
+                               s->ptr = NDBGETP(p);
+                               s->ptr1 = NDBGETP(p+NDBPLEN);
+                               s->type = Cptr;
+                       } else {                /* end of hash chain */
+                               if(fseek(db->b, s->ptr, 0) < 0)
+                                       break; 
+                               s->ptr = NDBNAP;
+                               t = ndbparse(db);
+                               if(t == 0)
+                                       break;
+                               if(s->t = match(t, attr, val)){
+                                       ndbsetmalloctag(t, getcallerpc(&s));
+                                       return t;
+                               }
+                               ndbfree(t);
+                               break;
+                       }
+               }
+       }
+
+nextfile:
+
+       /* nothing left to search? */
+       s->ptr = NDBNAP;
+       if(db->next == 0)
+               return 0;
+
+       /* advance search to next db file */
+       t = ndbsearch(db->next, s, attr, val);
+       ndbsetmalloctag(t, getcallerpc(&s));
+       return t;
+}
diff --git a/user/ndblib/ndbhf.h b/user/ndblib/ndbhf.h
new file mode 100755 (executable)
index 0000000..53efd74
--- /dev/null
@@ -0,0 +1,38 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+/* a hash file */
+
+struct ndbhf
+{
+       struct ndbhf    *next;
+
+       int     fd;
+       uint32_t        dbmtime;        /* mtime of data base */
+       int     hlen;           /* length (in entries) of hash table */
+       char    attr[Ndbalen];  /* attribute hashed */
+
+       uint8_t buf[256];       /* hash file buffer */
+       long    off;            /* offset of first byte of buffer */
+       int     len;            /* length of valid data in buffer */
+};
+
+char*          _ndbparsetuple(char*, struct ndbtuple**);
+struct ndbtuple*       _ndbparseline(char*);
+
+#define ISWHITE(x) ((x) == ' ' || (x) == '\t' || (x) == '\r')
+#define EATWHITE(x) while(ISWHITE(*(x)))(x)++
+
+extern struct ndbtuple *_ndbtfree;
+
+/* caches */
+void   _ndbcacheflush(struct ndb *db);
+int    _ndbcachesearch(struct ndb *db, struct ndbs *s, char *attr, char *val,
+                          struct ndbtuple **t);
+struct ndbtuple* _ndbcacheadd(struct ndb *db, struct ndbs *s, char *attr, char *val,
+                             struct ndbtuple *t);
diff --git a/user/ndblib/ndbipinfo.c b/user/ndblib/ndbipinfo.c
new file mode 100755 (executable)
index 0000000..6c04db8
--- /dev/null
@@ -0,0 +1,271 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <libip.h>
+#include <ndb.h>
+
+enum
+{
+       Ffound= 1<<0,
+       Fignore=1<<1,
+       Faddr=  1<<2,
+};
+
+static struct ndbtuple*        filter(struct ndb *db, struct ndbtuple *t,
+                                     struct ndbtuple *f);
+static struct ndbtuple*        mkfilter(int argc, char **argv);
+static int             filtercomplete(struct ndbtuple *f);
+static struct ndbtuple*        toipaddr(struct ndb *db, struct ndbtuple *t);
+static int             prefixlen(uint8_t *ip);
+static struct ndbtuple*        subnet(struct ndb *db, uint8_t *net,
+                                     struct ndbtuple *f, int prefix);
+
+/* make a filter to be used in filter */
+static struct ndbtuple*
+mkfilter(int argc, char **argv)
+{
+       struct ndbtuple *t, *first, *last;
+       char *p;
+
+       last = first = NULL;
+       while(argc-- > 0){
+               t = ndbnew(0, 0);
+               if(first)
+                       last->entry = t;
+               else
+                       first = t;
+               last = t;
+               p = *argv++;
+               if(*p == '@'){                  /* @attr=val ? */
+                       t->ptr |= Faddr;        /* return resolved address(es) */
+                       p++;
+               }
+               strncpy(t->attr, p, sizeof(t->attr)-1);
+       }
+       ndbsetmalloctag(first, getcallerpc(&argc));
+       return first;
+}
+
+/* return true if every pair of filter has been used */
+static int
+filtercomplete(struct ndbtuple *f)
+{
+       for(; f; f = f->entry)
+               if((f->ptr & Fignore) == 0)
+                       return 0;
+       return 1;
+}
+
+/* set the attribute of all entries in a tuple */
+static struct ndbtuple*
+setattr(struct ndbtuple *t, char *attr)
+{
+       struct ndbtuple *nt;
+
+       for(nt = t; nt; nt = nt->entry)
+               strcpy(nt->attr, attr);
+       return t;
+}
+
+/*
+ *  return only the attr/value pairs in t maching the filter, f.
+ *  others are freed.  line structure is preserved.
+ */
+static struct ndbtuple*
+filter(struct ndb *db, struct ndbtuple *t, struct ndbtuple *f)
+{
+       struct ndbtuple *nt, *nf, *next;
+
+       /* filter out what we don't want */
+       for(nt = t; nt; nt = next){
+               next = nt->entry;
+
+               /* look through filter */
+               for(nf = f; nf != NULL; nf = nf->entry){
+                       if(!(nf->ptr&Fignore) && strcmp(nt->attr, nf->attr) == 0)
+                               break;
+               }
+               if(nf == NULL){
+                       /* remove nt from t */
+                       t = ndbdiscard(t, nt);
+               } else {
+                       if(nf->ptr & Faddr)
+                               t = ndbsubstitute(t, nt, setattr(ndbgetipaddr(db, nt->val), nt->attr));
+                       nf->ptr |= Ffound;
+               }
+       }
+
+       /* remember filter etnries that matched */
+       for(nf = f; nf != NULL; nf = nf->entry)
+               if(nf->ptr & Ffound)
+                       nf->ptr = (nf->ptr & ~Ffound) | Fignore;
+
+       ndbsetmalloctag(t, getcallerpc(&db));
+       return t;
+}
+
+static int
+prefixlen(uint8_t *ip)
+{
+       int y, i;
+
+       for(y = IPaddrlen-1; y >= 0; y--)
+               for(i = 8; i > 0; i--)
+                       if(ip[y] & (1<<(8-i)))
+                               return y*8 + i;
+       return 0;
+}
+
+/*
+ *  look through a containing subset
+ */
+static struct ndbtuple*
+subnet(struct ndb *db, uint8_t *net, struct ndbtuple *f, int prefix)
+{
+       struct ndbs s;
+       struct ndbtuple *t, *nt, *xt;
+       char netstr[128];
+       uint8_t mask[IPaddrlen];
+       int masklen;
+
+       t = NULL;
+       sprintf(netstr, "%I", net);
+       nt = ndbsearch(db, &s, "ip", netstr);
+       while(nt != NULL){
+               xt = ndbfindattr(nt, nt, "ipnet");
+               if(xt){
+                       xt = ndbfindattr(nt, nt, "ipmask");
+                       if(xt)
+                               parseipmask(mask, xt->val);
+                       else
+                               ipmove(mask, defmask(net));
+                       masklen = prefixlen(mask);
+                       if(masklen <= prefix){
+                               t = ndbconcatenate(t, filter(db, nt, f));
+                               nt = NULL;
+                       }
+               }
+               ndbfree(nt);
+               nt = ndbsnext(&s, "ip", netstr);
+       }
+       ndbsetmalloctag(t, getcallerpc(&db));
+       return t;
+}
+
+/*
+ *  fill in all the requested attributes for a system.
+ *  if the system's entry doesn't have all required,
+ *  walk through successively more inclusive networks
+ *  for inherited attributes.
+ */
+struct ndbtuple*
+ndbipinfo(struct ndb *db, char *attr, char *val, char **alist, int n)
+{
+       struct ndbtuple *t, *nt, *f;
+       struct ndbs s;
+       char *ipstr;
+       uint8_t net[IPaddrlen], ip[IPaddrlen];
+       int prefix, smallestprefix, force;
+       int64_t r;
+
+#if 0
+       /* just in case */
+       fmtinstall('I', eipfmt);
+       fmtinstall('M', eipfmt);
+#endif
+
+       /* get needed attributes */
+       f = mkfilter(n, alist);
+
+       /*
+        *  first look for a matching entry with an ip address
+        */
+       t = NULL;
+       ipstr = ndbgetvalue(db, &s, attr, val, "ip", &nt);
+       if(ipstr == NULL){
+               /* none found, make one up */
+               if(strcmp(attr, "ip") != 0) {
+                       ndbfree(f);
+                       return NULL;    
+               }
+               t = ndbnew("ip", val);
+               t->line = t;
+               t->entry = NULL;
+               r = parseip(net, val);
+               if(r == -1)
+                       ndbfree(t);
+       } else {
+               /* found one */
+               while(nt != NULL){
+                       nt = ndbreorder(nt, s.t);
+                       t = ndbconcatenate(t, nt);
+                       nt = ndbsnext(&s, attr, val);
+               }
+               r = parseip(net, ipstr);
+               free(ipstr);
+       }
+       if(r < 0){
+               ndbfree(f);
+               return NULL;
+       }
+       ipmove(ip, net);
+       t = filter(db, t, f);
+
+       /*
+        *  now go through subnets to fill in any missing attributes
+        */
+       if(isv4(net)){
+               prefix = 127;
+               smallestprefix = 100;
+               force = 0;
+       } else {
+               /* in v6, the last 8 bytes have no structure (we hope) */
+               prefix = 64;
+               smallestprefix = 2;
+               memset(net+8, 0, 8);
+               force = 1;
+       }
+
+       /*
+        *  to find a containing network, keep turning off
+        *  the lower bit and look for a network with
+        *  that address and a shorter mask.  tedius but
+        *  complete, we may need to find a trick to speed this up.
+        */
+       for(; prefix >= smallestprefix; prefix--){
+               if(filtercomplete(f))
+                       break;
+               if(!force && (net[prefix/8] & (1<<(7-(prefix%8)))) == 0)
+                       continue;
+               force = 0;
+               net[prefix/8] &= ~(1<<(7-(prefix%8)));
+               t = ndbconcatenate(t, subnet(db, net, f, prefix));
+       }
+
+       /*
+        *  if there's an unfulfilled ipmask, make one up
+        */
+       nt = ndbfindattr(f, f, "ipmask");
+       if(nt && !(nt->ptr & Fignore)){
+               char x[64];
+
+               snprintf(x, sizeof(x), "%M", defmask(ip));
+               t = ndbconcatenate(t, ndbnew("ipmask", x));
+       }
+
+       ndbfree(f);
+       ndbsetmalloctag(t, getcallerpc(&db));
+       return t;
+}
diff --git a/user/ndblib/ndblookval.c b/user/ndblib/ndblookval.c
new file mode 100755 (executable)
index 0000000..a822730
--- /dev/null
@@ -0,0 +1,57 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <libip.h>
+#include <ndb.h>
+
+/*
+ *  Look for a pair with the given attribute.  look first on the same line,
+ *  then in the whole entry.
+ */
+struct ndbtuple*
+ndbfindattr(struct ndbtuple *entry, struct ndbtuple *line, char *attr)
+{
+       struct ndbtuple *nt;
+
+       /* first look on same line (closer binding) */
+       for(nt = line; nt;){
+               if(strcmp(attr, nt->attr) == 0)
+                       return nt;
+               nt = nt->line;
+               if(nt == line)
+                       break;
+       }
+
+       /* search whole tuple */
+       for(nt = entry; nt; nt = nt->entry)
+               if(strcmp(attr, nt->attr) == 0)
+                       return nt;
+
+       return NULL;
+}
+
+struct ndbtuple*
+ndblookval(struct ndbtuple *entry,
+          struct ndbtuple *line, char *attr, char *to)
+{
+       struct ndbtuple *t;
+
+       t = ndbfindattr(entry, line, attr);
+       if(t != NULL){
+               strncpy(to, t->val, Ndbvlen-1);
+               to[Ndbvlen-1] = 0;
+       }
+       return t;
+}
diff --git a/user/ndblib/ndbopen.c b/user/ndblib/ndbopen.c
new file mode 100755 (executable)
index 0000000..2e5430f
--- /dev/null
@@ -0,0 +1,192 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <libip.h>
+#include <dir.h>
+#include <ndb.h>
+#include "ndbhf.h"
+
+static struct ndb*     doopen(char*);
+static void    hffree(struct ndb*);
+
+static char *deffile = "/lib/ndb/local";
+
+/*
+ *  the database entry in 'file' indicates the list of files
+ *  that makeup the database.  Open each one and search in
+ *  the same order.
+ */
+struct ndb*
+ndbopen(char *file)
+{
+       struct ndb *db, *first, *last;
+       struct ndbs s;
+       struct ndbtuple *t, *nt;
+
+       if(file == 0)
+               file = deffile;
+       db = doopen(file);
+       if(db == 0)
+               return 0;
+       first = last = db;
+       t = ndbsearch(db, &s, "database", "");
+       fseek(db->b, 0, 0);
+       if(t == 0)
+               return db;
+       for(nt = t; nt; nt = nt->entry){
+               if(strcmp(nt->attr, "file") != 0)
+                       continue;
+               if(strcmp(nt->val, file) == 0){
+                       /* default file can be reordered in the list */
+                       if(first->next == 0)
+                               continue;
+                       if(strcmp(first->file, file) == 0){
+                               db = first;
+                               first = first->next;
+                               last->next = db;
+                               db->next = 0;
+                               last = db;
+                       }
+                       continue;
+               }
+               db = doopen(nt->val);
+               if(db == 0)
+                       continue;
+               last->next = db;
+               last = db;
+       }
+       ndbfree(t);
+       return first;
+}
+
+/*
+ *  open a single file
+ */
+static struct ndb*
+doopen(char *file)
+{
+       struct ndb *db;
+
+       db = (struct ndb*)calloc(sizeof(struct ndb), 1);
+       if(db == 0)
+               return 0;
+       memset(db, 0, sizeof(struct ndb));
+       strncpy(db->file, file, sizeof(db->file)-1);
+
+       if(ndbreopen(db) < 0){
+               free(db);
+               return 0;
+       }
+
+       return db;
+}
+
+/*
+ *  dump any cached information, forget the hash tables, and reopen a single file
+ */
+int
+ndbreopen(struct ndb *db)
+{
+       int fd;
+       struct dir *d;
+
+       /* forget what we know about the open files */
+       if(db->isopen){
+               _ndbcacheflush(db);
+               hffree(db);
+               fclose(db->b);
+               db->mtime = 0;
+               db->isopen = 0;
+       }
+
+       /* try the open again */
+       db->b = fopen(db->file, "r");
+       if(! db->b)
+               return -1;
+#if 0
+       d = dirfstat(fd);
+       if(d == NULL){
+               close(fd);
+               return -1;
+       }
+
+       db->qid = d->qid;
+       db->mtime = d->mtime;
+       db->length = d->length;
+       free(d);
+#endif
+       db->isopen = 1;
+       return 0;
+}
+
+/*
+ *  close the database files
+ */
+void
+ndbclose(struct ndb *db)
+{
+       struct ndb *nextdb;
+
+       for(; db; db = nextdb){
+               nextdb = db->next;
+               _ndbcacheflush(db);
+               hffree(db);
+               fclose(db->b);
+               free(db);
+       }
+}
+
+/*
+ *  free the hash files belonging to a db
+ */
+static void
+hffree(struct ndb *db)
+{
+       struct ndbhf *hf, *next;
+
+       for(hf = db->hf; hf; hf = next){
+               next = hf->next;
+               close(hf->fd);
+               free(hf);
+       }
+       db->hf = 0;
+}
+
+/*
+ *  return true if any part of the database has changed
+ */
+int
+ndbchanged(struct ndb *db)
+{
+       return 1;
+#if 0
+       struct ndb *ndb;
+       struct dir *d;
+
+/* FIX ME */
+       for(ndb = db; ndb != NULL; ndb = ndb->next){
+               d = dirfstat(Bfildes(&ndb->b));
+               if(d == NULL)
+                       continue;
+               if(ndb->qid.path != d->qid.path
+               || ndb->qid.vers != d->qid.vers){
+                       free(d);
+                       return 1;
+               }
+               free(d);
+       }
+       return 0;
+#endif
+}
diff --git a/user/ndblib/ndbparse.c b/user/ndblib/ndbparse.c
new file mode 100755 (executable)
index 0000000..d40b791
--- /dev/null
@@ -0,0 +1,69 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <libip.h>
+#include <ndb.h>
+
+/*
+ *  Parse a data base entry.  Entries may span multiple
+ *  lines.  An entry starts on a left margin.  All subsequent
+ *  lines must be indented by white space.  An entry consists
+ *  of tuples of the forms:
+ *     attribute-name
+ *     attribute-name=value
+ *     attribute-name="value with white space"
+ *
+ *  The parsing returns a 2-dimensional structure.  The first
+ *  dimension joins all tuples. All tuples on the same line
+ *  form a ring along the second dimension.
+ */
+
+/*
+ *  parse the next entry in the file
+ */
+struct ndbtuple*
+ndbparse(struct ndb *db)
+{
+       char *line;
+       struct ndbtuple *t;
+       struct ndbtuple *first, *last;
+       int len;
+
+       first = last = 0;
+       for(;;){
+               if((line = fgets(db->buf, sizeof(db->buf),db->b)) == 0)
+                       break;
+               len = strlen(db->buf);
+               if(line[len-1] != '\n')
+                       break;
+               if(first && !isspace(*line) && *line != '#'){
+                       fseek(db->b, -len, 1);
+                       break;
+               }
+               t = _ndbparseline(line);
+               if(t == 0)
+                       continue;
+               if(first)
+                       last->entry = t;
+               else
+                       first = t;
+               last = t;
+               while(last->entry)
+                       last = last->entry;
+       }
+       ndbsetmalloctag(first, getcallerpc(&db));
+       return first;
+}
diff --git a/user/ndblib/ndbreorder.c b/user/ndblib/ndbreorder.c
new file mode 100755 (executable)
index 0000000..35d1364
--- /dev/null
@@ -0,0 +1,66 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <libip.h>
+#include <ndb.h>
+
+/*
+ *  reorder the tuple to put x's line first in the entry and x fitst in its line
+ */
+struct ndbtuple*
+ndbreorder(struct ndbtuple *t, struct ndbtuple *x)
+{
+       struct ndbtuple *nt;
+       struct ndbtuple *last, *prev;
+
+       /* if x is first, we're done */
+       if(x == t)
+               return t;
+
+       /* find end of x's line */
+       for(last = x; last->line == last->entry; last = last->line)
+               ;
+
+       /* rotate to make this line first */
+       if(last->line != t){
+
+               /* detach this line and everything after it from the entry */
+               for(nt = t; nt->entry != last->line; nt = nt->entry)
+                       ;
+               nt->entry = NULL;
+       
+               /* switch */
+               for(nt = last; nt->entry != NULL; nt = nt->entry)
+                       ;
+               nt->entry = t;
+       }
+
+       /* rotate line to make x first */
+       if(x != last->line){
+
+               /* find entry before x */
+               for(prev = last; prev->line != x; prev = prev->line);
+                       ;
+
+               /* detach line */
+               nt = last->entry;
+               last->entry = last->line;
+
+               /* reattach */
+               prev->entry = nt;
+       }
+
+       return x;
+}
diff --git a/user/ndblib/ndbsubstitute.c b/user/ndblib/ndbsubstitute.c
new file mode 100755 (executable)
index 0000000..869d592
--- /dev/null
@@ -0,0 +1,59 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <libip.h>
+#include <ndb.h>
+
+/* replace a in t with b, the line structure in b is lost, c'est la vie */
+struct ndbtuple*
+ndbsubstitute(struct ndbtuple *t, struct ndbtuple *a, struct ndbtuple *b)
+{
+       struct ndbtuple *nt;
+
+       if(a == b){
+               ndbsetmalloctag(t, getcallerpc(&t));
+               return t;
+       }
+       if(b == NULL){
+               t = ndbdiscard(t, a);
+               ndbsetmalloctag(t, getcallerpc(&t));
+               return t;
+       }
+
+       /* all pointers to a become pointers to b */
+       for(nt = t; nt != NULL; nt = nt->entry){
+               if(nt->line == a)
+                       nt->line = b;
+               if(nt->entry == a)
+                       nt->entry = b;
+       }
+
+       /* end of b chain points to a's successors */
+       for(nt = b; nt->entry; nt = nt->entry)
+               nt->line = nt->entry;
+       nt->line = a->line;
+       nt->entry = a->entry;
+
+       a->entry = NULL;
+       ndbfree(a);
+
+       if(a == t){
+               ndbsetmalloctag(b, getcallerpc(&t));
+               return b;
+       }else{
+               ndbsetmalloctag(t, getcallerpc(&t));
+               return t;
+       }
+}
diff --git a/user/ndblib/read9pmsg.c b/user/ndblib/read9pmsg.c
new file mode 100644 (file)
index 0000000..757b06f
--- /dev/null
@@ -0,0 +1,68 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <libip.h>
+#include <dir.h>
+#include <fcall.h>
+#include <ndb.h>
+
+long
+readn(int f, void *av, long n)
+{
+       char *a;
+       long m, t;
+
+       a = av;
+       t = 0;
+       while(t < n){
+               m = read(f, a+t, n-t);
+               if(m <= 0){
+                       if(t == 0)
+                               return m;
+                       break;
+               }
+               t += m;
+       }
+       return t;
+}
+
+int
+read9pmsg(int fd, void *abuf, unsigned int n)
+{
+       int m, len;
+       uint8_t *buf;
+
+       buf = abuf;
+
+       /* read count */
+       m = readn(fd, buf, BIT32SZ);
+       if(m != BIT32SZ){
+               if(m < 0)
+                       return -1;
+               return 0;
+       }
+
+       len = GBIT32(buf);
+       if(len <= BIT32SZ || len > n){
+#warning "implement werrstr in user mode"
+               /*werrstr(*/fprintf(stderr,"bad length in 9P2000 message header");
+               return -1;
+       }
+       len -= BIT32SZ;
+       m = readn(fd, buf+BIT32SZ, len);
+       if(m < len)
+               return 0;
+       return BIT32SZ+m;
+}
diff --git a/user/ndblib/setnetmtpt.c b/user/ndblib/setnetmtpt.c
new file mode 100755 (executable)
index 0000000..1421966
--- /dev/null
@@ -0,0 +1,31 @@
+/* 
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+#include <stdlib.h>
+
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <libip.h>
+#include <ndb.h>
+
+void
+setnetmtpt(char *net, int n, char *x)
+{
+       if(x == NULL)
+               x = "/net";
+
+       if(*x == '/'){
+               strncpy(net, x, n);
+               net[n-1] = 0;
+       } else {
+               snprintf(net, n, "/net%s", x);
+       }
+}