Add support for user-level 9P servers/clients and various bug fixes to go with them.
diff --git a/src/lib9/announce.c b/src/lib9/announce.c
index 04c712d..9f07bd2 100644
--- a/src/lib9/announce.c
+++ b/src/lib9/announce.c
@@ -5,13 +5,14 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <sys/un.h>
+#include <errno.h>
 
 #undef sun
 #define sun sockun
 extern int _p9dialparse(char*, char**, char**, u32int*, int*);
 
-static int
-getfd(char *dir)
+int
+_p9netfd(char *dir)
 {
 	int fd;
 
@@ -83,7 +84,6 @@
 	if(proto == SOCK_STREAM){
 		listen(s, 8);
 		putfd(dir, s);
-print("announce dir: %s\n", dir);
 	}
 	return s;
 
@@ -95,9 +95,21 @@
 		return -1;
 	sn = sizeof sun;
 	if(bind(s, (struct sockaddr*)&sun, sizeof sun) < 0){
+		if(errno == EADDRINUSE
+		&& connect(s, (struct sockaddr*)&sun, sizeof sun) < 0
+		&& errno == ECONNREFUSED){
+			/* dead socket, so remove it */
+			remove(unix);
+			close(s);
+			if((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+				return -1;
+			if(bind(s, (struct sockaddr*)&sun, sizeof sun) >= 0)
+				goto Success;
+		}
 		close(s);
 		return -1;
 	}
+Success:
 	listen(s, 8);
 	putfd(dir, s);
 	return s;
@@ -108,18 +120,15 @@
 {
 	int fd;
 
-	if((fd = getfd(dir)) < 0){
+	if((fd = _p9netfd(dir)) < 0){
 		werrstr("bad 'directory' in listen: %s", dir);
 		return -1;
 	}
 
-print("accept %d", fd);
 	if((fd = accept(fd, nil, nil)) < 0)
 		return -1;
-print(" -> %d\n", fd);
 
 	putfd(newdir, fd);
-print("listen dir: %s\n", newdir);
 	return fd;
 }
 
@@ -128,7 +137,7 @@
 {
 	int fd;
 
-	if((fd = getfd(dir)) < 0){
+	if((fd = _p9netfd(dir)) < 0){
 		werrstr("bad 'directory' in accept");
 		return -1;
 	}
diff --git a/src/lib9/await.c b/src/lib9/await.c
index 89c695a..5f2d58b 100644
--- a/src/lib9/await.c
+++ b/src/lib9/await.c
@@ -45,6 +45,7 @@
 #endif
 	SIGUSR1,		"sys: usr1",
 	SIGUSR2,		"sys: usr2",
+	SIGPIPE,		"sys: write on closed pipe",
 };
 	
 char*
diff --git a/src/lib9/convM2S.c b/src/lib9/convM2S.c
index fcdcd42..920775e 100644
--- a/src/lib9/convM2S.c
+++ b/src/lib9/convM2S.c
@@ -138,6 +138,7 @@
 		break;
 
 	case Topen:
+	case Topenfd:
 		if(p+BIT32SZ+BIT8SZ > ep)
 			return 0;
 		f->fid = GBIT32(p);
@@ -260,6 +261,7 @@
 		break;
 
 	case Ropen:
+	case Ropenfd:
 	case Rcreate:
 		p = gqid(p, ep, &f->qid);
 		if(p == nil)
@@ -268,6 +270,12 @@
 			return 0;
 		f->iounit = GBIT32(p);
 		p += BIT32SZ;
+		if(f->type == Ropenfd){
+			if(p+BIT32SZ > ep)
+				return 0;
+			f->unixfd = GBIT32(p);
+			p += BIT32SZ;
+		}
 		break;
 
 	case Rread:
diff --git a/src/lib9/convS2M.c b/src/lib9/convS2M.c
index 9acdcfa..6e9d271 100644
--- a/src/lib9/convS2M.c
+++ b/src/lib9/convS2M.c
@@ -92,6 +92,7 @@
 		break;
 
 	case Topen:
+	case Topenfd:
 		n += BIT32SZ;
 		n += BIT8SZ;
 		break;
@@ -164,6 +165,12 @@
 		n += BIT32SZ;
 		break;
 
+	case Ropenfd:
+		n += QIDSZ;
+		n += BIT32SZ;
+		n += BIT32SZ;
+		break;
+
 	case Rread:
 		n += BIT32SZ;
 		n += f->count;
@@ -257,6 +264,7 @@
 		break;
 
 	case Topen:
+	case Topenfd:
 		PBIT32(p, f->fid);
 		p += BIT32SZ;
 		PBIT8(p, f->mode);
@@ -347,9 +355,14 @@
 
 	case Ropen:
 	case Rcreate:
+	case Ropenfd:
 		p = pqid(p, &f->qid);
 		PBIT32(p, f->iounit);
 		p += BIT32SZ;
+		if(f->type == Ropenfd){
+			PBIT32(p, f->unixfd);
+			p += BIT32SZ;
+		}
 		break;
 
 	case Rread:
diff --git a/src/lib9/create.c b/src/lib9/create.c
index abef0c3..bdad5f6 100644
--- a/src/lib9/create.c
+++ b/src/lib9/create.c
@@ -1,8 +1,54 @@
 #include <u.h>
+#define NOPLAN9DEFINES
 #include <libc.h>
+#include <sys/stat.h>
+
+extern char *_p9translate(char*);
 
 int
-create(char *path, int mode, ulong perm)
+p9create(char *xpath, int mode, ulong perm)
 {
-	return open(path, mode|O_CREAT|O_TRUNC, perm);
+	int fd, cexec, umode, rclose;
+	char *path;
+
+	if((path = _p9translate(xpath)) == nil)
+		return -1;
+
+	cexec = mode&OCEXEC;
+	rclose = mode&ORCLOSE;
+	mode &= ~(ORCLOSE|OCEXEC);
+
+	/* XXX should get mode mask right? */
+	fd = -1;
+	if(perm&DMDIR){
+		if(mode != OREAD){
+			werrstr("bad mode in directory create");
+			goto out;
+		}
+		if(mkdir(path, perm&0777) < 0)
+			goto out;
+		fd = open(path, O_RDONLY);
+	}else{
+		umode = (mode&3)|O_CREAT|O_TRUNC;
+		mode &= ~(3|OTRUNC);
+		if(mode&OEXCL){
+			umode |= O_EXCL;
+			mode &= ~OEXCL;
+		}
+		if(mode){
+			werrstr("unsupported mode in create");
+			goto out;
+		}
+		fd = open(path, umode, perm);
+	}
+out:
+	if(fd >= 0){
+		if(cexec)
+			fcntl(fd, F_SETFL, FD_CLOEXEC);
+		if(rclose)
+			remove(path);
+	}
+	if(path != xpath)
+		free(path);
+	return fd;
 }
diff --git a/src/lib9/fcallfmt.c b/src/lib9/fcallfmt.c
index 4eef88d..592316f 100644
--- a/src/lib9/fcallfmt.c
+++ b/src/lib9/fcallfmt.c
@@ -74,9 +74,16 @@
 		seprint(buf, e, "Topen tag %ud fid %ud mode %d", tag, fid, f->mode);
 		break;
 	case Ropen:
-		seprint(buf, e, "Ropen tag %ud qid " QIDFMT " iounit %ud ", tag,
+		seprint(buf, e, "Ropen tag %ud qid " QIDFMT " iounit %ud", tag,
 			f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type), f->iounit);
 		break;
+	case Topenfd:	/* 98 */
+		seprint(buf, e, "Topenfd tag %ud fid %ud mode %d", tag, fid, f->mode);
+		break;
+	case Ropenfd:
+		seprint(buf, e, "Ropenfd tag %ud qid " QIDFMT " iounit %ud unixfd %d", tag,
+			f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type), f->iounit, f->unixfd);
+		break;
 	case Tcreate:	/* 114 */
 		seprint(buf, e, "Tcreate tag %ud fid %ud name %s perm %M mode %d", tag, fid, f->name, (ulong)f->perm, f->mode);
 		break;
diff --git a/src/lib9/getenv.c b/src/lib9/getenv.c
index c6ff716..2a2d139 100644
--- a/src/lib9/getenv.c
+++ b/src/lib9/getenv.c
@@ -13,3 +13,15 @@
 	return strdup(t);
 }
 
+int
+p9putenv(char *s, char *v)
+{
+	char *t;
+
+	t = smprint("%s=%s", s, v);
+	if(t == nil)
+		return -1;
+	putenv(t);
+	free(t);
+	return 0;
+}
diff --git a/src/lib9/mkfile b/src/lib9/mkfile
index 14d8c0e..a0e75fc 100644
--- a/src/lib9/mkfile
+++ b/src/lib9/mkfile
@@ -8,6 +8,8 @@
 	_p9dialparse.$O\
 	_p9dir.$O\
 	_p9proc.$O\
+	_p9translate.$O\
+	access.$O\
 	announce.$O\
 	argv0.$O\
 	atexit.$O\
@@ -40,11 +42,13 @@
 	getcallerpc-$OBJTYPE.$O\
 	getenv.$O\
 	getfields.$O\
+	getns.$O\
 	getuser.$O\
 	getwd.$O\
 	jmp.$O\
 	lock.$O\
 	main.$O\
+	malloc.$O\
 	malloctag.$O\
 	mallocz.$O\
 	nan.$O\
@@ -53,13 +57,18 @@
 	notify.$O\
 	nrand.$O\
 	nulldir.$O\
+	open.$O\
+	pipe.$O\
+	post9p.$O\
 	postnote.$O\
 	qlock.$O\
 	quote.$O\
+	read9pmsg.$O\
 	readn.$O\
 	rendez-$SYSNAME.$O\
 	rfork.$O\
 	seek.$O\
+	sendfd.$O\
 	sleep.$O\
 	strecpy.$O\
 	sysfatal.$O\
diff --git a/src/lib9/notify.c b/src/lib9/notify.c
index 460eabf..160755d 100644
--- a/src/lib9/notify.c
+++ b/src/lib9/notify.c
@@ -19,7 +19,7 @@
 #endif
 	SIGFPE,
 	SIGBUS,
-	SIGSEGV,
+/*	SIGSEGV,	*/
 	SIGSYS,
 	SIGPIPE,
 	SIGALRM,
diff --git a/src/lib9/rfork.c b/src/lib9/rfork.c
index 270c3cd..f3a2192 100644
--- a/src/lib9/rfork.c
+++ b/src/lib9/rfork.c
@@ -9,7 +9,7 @@
 	if((flags&(RFPROC|RFFDG|RFMEM)) == (RFPROC|RFFDG)){
 		/* check other flags before we commit */
 		flags &= ~(RFPROC|RFFDG);
-		if(flags & ~(RFNOTEG)){
+		if(flags & ~(RFNOTEG|RFNAMEG)){
 			werrstr("unknown flags %08ux in rfork", flags);
 			return -1;
 		}
@@ -17,11 +17,14 @@
 		if(pid != 0)
 			return pid;
 	}
-
 	if(flags&RFPROC){
-		werrstr("cannot use rfork to fork -- use ffork");
+		werrstr("cannot use rfork for shared memory -- use ffork");
 		return -1;
 	}
+	if(flags&RFNAMEG){
+		/* XXX set $NAMESPACE to a new directory */
+		flags &= ~RFNAMEG;
+	}
 	if(flags&RFNOTEG){
 		setpgid(0, getpid());
 		flags &= ~RFNOTEG;