summaryrefslogtreecommitdiff
path: root/sys/src/libc/9sys
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/libc/9sys
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/libc/9sys')
-rwxr-xr-xsys/src/libc/9sys/abort.c8
-rwxr-xr-xsys/src/libc/9sys/access.c33
-rwxr-xr-xsys/src/libc/9sys/announce.c274
-rwxr-xr-xsys/src/libc/9sys/convD2M.c95
-rwxr-xr-xsys/src/libc/9sys/convM2D.c94
-rwxr-xr-xsys/src/libc/9sys/convM2S.c315
-rwxr-xr-xsys/src/libc/9sys/convS2M.c389
-rwxr-xr-xsys/src/libc/9sys/cputime.c17
-rwxr-xr-xsys/src/libc/9sys/ctime.c304
-rwxr-xr-xsys/src/libc/9sys/dial.c492
-rwxr-xr-xsys/src/libc/9sys/dirfstat.c37
-rwxr-xr-xsys/src/libc/9sys/dirfwstat.c19
-rwxr-xr-xsys/src/libc/9sys/dirmodefmt.c48
-rwxr-xr-xsys/src/libc/9sys/dirread.c97
-rwxr-xr-xsys/src/libc/9sys/dirstat.c37
-rwxr-xr-xsys/src/libc/9sys/dirwstat.c19
-rwxr-xr-xsys/src/libc/9sys/fcallfmt.c234
-rwxr-xr-xsys/src/libc/9sys/fork.c8
-rwxr-xr-xsys/src/libc/9sys/getenv.c36
-rwxr-xr-xsys/src/libc/9sys/getnetconninfo.c133
-rwxr-xr-xsys/src/libc/9sys/getpid.c17
-rwxr-xr-xsys/src/libc/9sys/getppid.c17
-rwxr-xr-xsys/src/libc/9sys/getwd.c19
-rwxr-xr-xsys/src/libc/9sys/iounit.c27
-rwxr-xr-xsys/src/libc/9sys/mkfile64
-rwxr-xr-xsys/src/libc/9sys/nsec.c75
-rwxr-xr-xsys/src/libc/9sys/nulldir.c9
-rwxr-xr-xsys/src/libc/9sys/postnote.c32
-rwxr-xr-xsys/src/libc/9sys/privalloc.c45
-rwxr-xr-xsys/src/libc/9sys/pushssl.c44
-rwxr-xr-xsys/src/libc/9sys/pushtls.c99
-rwxr-xr-xsys/src/libc/9sys/putenv.c26
-rwxr-xr-xsys/src/libc/9sys/qlock.c363
-rwxr-xr-xsys/src/libc/9sys/read.c8
-rwxr-xr-xsys/src/libc/9sys/read9pmsg.c31
-rwxr-xr-xsys/src/libc/9sys/readv.c49
-rwxr-xr-xsys/src/libc/9sys/rerrstr.c13
-rwxr-xr-xsys/src/libc/9sys/sbrk.c35
-rwxr-xr-xsys/src/libc/9sys/setnetmtpt.c16
-rwxr-xr-xsys/src/libc/9sys/sysfatal.c28
-rwxr-xr-xsys/src/libc/9sys/syslog.c117
-rwxr-xr-xsys/src/libc/9sys/sysname.c21
-rwxr-xr-xsys/src/libc/9sys/time.c51
-rwxr-xr-xsys/src/libc/9sys/times.c60
-rwxr-xr-xsys/src/libc/9sys/tm2sec.c194
-rwxr-xr-xsys/src/libc/9sys/truerand.c17
-rwxr-xr-xsys/src/libc/9sys/wait.c32
-rwxr-xr-xsys/src/libc/9sys/waitpid.c21
-rwxr-xr-xsys/src/libc/9sys/werrstr.c14
-rwxr-xr-xsys/src/libc/9sys/write.c8
-rwxr-xr-xsys/src/libc/9sys/writev.c42
51 files changed, 4283 insertions, 0 deletions
diff --git a/sys/src/libc/9sys/abort.c b/sys/src/libc/9sys/abort.c
new file mode 100755
index 000000000..b0fd74cd5
--- /dev/null
+++ b/sys/src/libc/9sys/abort.c
@@ -0,0 +1,8 @@
+#include <u.h>
+#include <libc.h>
+void
+abort(void)
+{
+ while(*(int*)0)
+ ;
+}
diff --git a/sys/src/libc/9sys/access.c b/sys/src/libc/9sys/access.c
new file mode 100755
index 000000000..c9fee3432
--- /dev/null
+++ b/sys/src/libc/9sys/access.c
@@ -0,0 +1,33 @@
+#include <u.h>
+#include <libc.h>
+
+int
+access(char *name, int mode)
+{
+ int fd;
+ Dir *db;
+ static char omode[] = {
+ 0,
+ OEXEC,
+ OWRITE,
+ ORDWR,
+ OREAD,
+ OEXEC, /* only approximate */
+ ORDWR,
+ ORDWR /* only approximate */
+ };
+
+ if(mode == AEXIST){
+ db = dirstat(name);
+ free(db);
+ if(db != nil)
+ return 0;
+ return -1;
+ }
+ fd = open(name, omode[mode&7]);
+ if(fd >= 0){
+ close(fd);
+ return 0;
+ }
+ return -1;
+}
diff --git a/sys/src/libc/9sys/announce.c b/sys/src/libc/9sys/announce.c
new file mode 100755
index 000000000..b2252ed21
--- /dev/null
+++ b/sys/src/libc/9sys/announce.c
@@ -0,0 +1,274 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.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, ORDWR);
+ if(ctl<0){
+ werrstr("announce opening %s: %r", netdir);
+ return -1;
+ }
+ cp = strrchr(netdir, '/');
+ if(cp == nil){
+ werrstr("announce arg format %s", netdir);
+ close(ctl);
+ return -1;
+ }
+ *cp = 0;
+
+ /*
+ * find out which line we have
+ */
+ n = snprint(buf, sizeof(buf), "%s/", netdir);
+ m = read(ctl, &buf[n], sizeof(buf)-n-1);
+ if(m <= 0){
+ werrstr("announce reading %s: %r", netdir);
+ close(ctl);
+ return -1;
+ }
+ buf[n+m] = 0;
+
+ /*
+ * make the call
+ */
+ n = snprint(buf2, sizeof(buf2), "announce %s", naddr);
+ if(write(ctl, buf2, n)!=n){
+ werrstr("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
+ */
+ snprint(buf, sizeof(buf), "%s/listen", dir);
+ ctl = open(buf, ORDWR);
+ if(ctl < 0){
+ werrstr("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 == nil){
+ close(ctl);
+ werrstr("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);
+ werrstr("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 == nil)
+ num = dir;
+ else
+ num++;
+
+ n = snprint(buf, sizeof(buf), "accept %s", num);
+ write(ctl, buf, n); /* ignore return value, network might not need accepts */
+
+ snprint(buf, sizeof(buf), "%s/data", dir);
+ return open(buf, ORDWR);
+}
+
+/*
+ * 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++;
+ snprint(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;
+
+ USED(nf);
+
+ /* parse the protocol */
+ strncpy(proto, addr, sizeof(proto));
+ proto[sizeof(proto)-1] = 0;
+ p = strchr(proto, '!');
+ if(p)
+ *p++ = 0;
+
+ snprint(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){
+ werrstr("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)){
+ werrstr("bad dial string: %s", addr);
+ return -1;
+ }
+ strncpy(netdir, addr, i);
+ netdir[i] = 0;
+ addr = p2 + 1;
+ }
+
+ /*
+ * ask the connection server
+ */
+ snprint(buf, sizeof(buf), "%s/cs", netdir);
+ fd = open(buf, ORDWR);
+ if(fd < 0)
+ return identtrans(netdir, addr, naddr, na, file, nf);
+ if(write(fd, addr, strlen(addr)) < 0){
+ close(fd);
+ return -1;
+ }
+ seek(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 == nil)
+ p = buf;
+ else
+ p++;
+ }
+ snprint(file, nf, "%s/%s", netdir, p);
+ return 0;
+}
diff --git a/sys/src/libc/9sys/convD2M.c b/sys/src/libc/9sys/convD2M.c
new file mode 100755
index 000000000..dfc0e5d79
--- /dev/null
+++ b/sys/src/libc/9sys/convD2M.c
@@ -0,0 +1,95 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+uint
+sizeD2M(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;
+}
+
+uint
+convD2M(Dir *d, uchar *buf, uint nbuf)
+{
+ uchar *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/sys/src/libc/9sys/convM2D.c b/sys/src/libc/9sys/convM2D.c
new file mode 100755
index 000000000..6f4b4bd93
--- /dev/null
+++ b/sys/src/libc/9sys/convM2D.c
@@ -0,0 +1,94 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+int
+statcheck(uchar *buf, uint nbuf)
+{
+ uchar *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[] = "";
+
+uint
+convM2D(uchar *buf, uint nbuf, Dir *d, char *strs)
+{
+ uchar *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/sys/src/libc/9sys/convM2S.c b/sys/src/libc/9sys/convM2S.c
new file mode 100755
index 000000000..fcdcd42d6
--- /dev/null
+++ b/sys/src/libc/9sys/convM2S.c
@@ -0,0 +1,315 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+static
+uchar*
+gstring(uchar *p, uchar *ep, char **s)
+{
+ uint n;
+
+ if(p+BIT16SZ > ep)
+ return nil;
+ n = GBIT16(p);
+ p += BIT16SZ - 1;
+ if(p+n+1 > ep)
+ return nil;
+ /* 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
+uchar*
+gqid(uchar *p, uchar *ep, Qid *q)
+{
+ if(p+QIDSZ > ep)
+ return nil;
+ 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 nil if they would reach beyond buffer.
+ * main switch statement checks range and also can fall through
+ * to test at end of routine.
+ */
+uint
+convM2S(uchar *ap, uint nap, Fcall *f)
+{
+ uchar *p, *ep;
+ uint 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 == nil)
+ break;
+ p = gstring(p, ep, &f->aname);
+ if(p == nil)
+ 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 == nil)
+ break;
+ p = gstring(p, ep, &f->aname);
+ if(p == nil)
+ 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 == nil)
+ 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 == nil)
+ 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 == nil)
+ break;
+ break;
+
+ case Rattach:
+ p = gqid(p, ep, &f->qid);
+ if(p == nil)
+ 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 == nil)
+ break;
+ }
+ break;
+
+ case Ropen:
+ case Rcreate:
+ p = gqid(p, ep, &f->qid);
+ if(p == nil)
+ 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==nil || p>ep)
+ return 0;
+ if(ap+size == p)
+ return size;
+ return 0;
+}
diff --git a/sys/src/libc/9sys/convS2M.c b/sys/src/libc/9sys/convS2M.c
new file mode 100755
index 000000000..fb5f596ce
--- /dev/null
+++ b/sys/src/libc/9sys/convS2M.c
@@ -0,0 +1,389 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+static
+uchar*
+pstring(uchar *p, char *s)
+{
+ uint n;
+
+ if(s == nil){
+ 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
+uchar*
+pqid(uchar *p, Qid *q)
+{
+ PBIT8(p, q->type);
+ p += BIT8SZ;
+ PBIT32(p, q->vers);
+ p += BIT32SZ;
+ PBIT64(p, q->path);
+ p += BIT64SZ;
+ return p;
+}
+
+static
+uint
+stringsz(char *s)
+{
+ if(s == nil)
+ return BIT16SZ;
+
+ return BIT16SZ+strlen(s);
+}
+
+uint
+sizeS2M(Fcall *f)
+{
+ uint 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;
+}
+
+uint
+convS2M(Fcall *f, uchar *ap, uint nap)
+{
+ uchar *p;
+ uint i, size;
+
+ size = sizeS2M(f);
+ if(size == 0)
+ return 0;
+ if(size > nap)
+ return 0;
+
+ p = (uchar*)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/sys/src/libc/9sys/cputime.c b/sys/src/libc/9sys/cputime.c
new file mode 100755
index 000000000..1e05634aa
--- /dev/null
+++ b/sys/src/libc/9sys/cputime.c
@@ -0,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+
+#define HZ 1000
+
+double
+cputime(void)
+{
+ long t[4];
+ long times(long*);
+ int i;
+
+ times(t);
+ for(i=1; i<4; i++)
+ t[0] += t[i];
+ return t[0] / (double)HZ;
+}
diff --git a/sys/src/libc/9sys/ctime.c b/sys/src/libc/9sys/ctime.c
new file mode 100755
index 000000000..d841b64e9
--- /dev/null
+++ b/sys/src/libc/9sys/ctime.c
@@ -0,0 +1,304 @@
+/*
+ * This routine converts time as follows.
+ * The epoch is 0000 Jan 1 1970 GMT.
+ * The argument time is in seconds since then.
+ * The localtime(t) entry returns a pointer to an array
+ * containing
+ *
+ * seconds (0-59)
+ * minutes (0-59)
+ * hours (0-23)
+ * day of month (1-31)
+ * month (0-11)
+ * year-1970
+ * weekday (0-6, Sun is 0)
+ * day of the year
+ * daylight savings flag
+ *
+ * The routine gets the daylight savings time from the environment.
+ *
+ * asctime(tvec))
+ * where tvec is produced by localtime
+ * returns a ptr to a character string
+ * that has the ascii time in the form
+ *
+ * \\
+ * Thu Jan 01 00:00:00 GMT 1970n0
+ * 012345678901234567890123456789
+ * 0 1 2
+ *
+ * ctime(t) just calls localtime, then asctime.
+ */
+
+#include <u.h>
+#include <libc.h>
+
+static char dmsize[12] =
+{
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+/*
+ * The following table is used for 1974 and 1975 and
+ * gives the day number of the first day after the Sunday of the
+ * change.
+ */
+
+static int dysize(int);
+static void ct_numb(char*, int);
+
+#define TZSIZE 150
+static void readtimezone(void);
+static int rd_name(char**, char*);
+static int rd_long(char**, long*);
+static
+struct
+{
+ char stname[4];
+ char dlname[4];
+ long stdiff;
+ long dldiff;
+ long dlpairs[TZSIZE];
+} timezone;
+
+char*
+ctime(long t)
+{
+ return asctime(localtime(t));
+}
+
+Tm*
+localtime(long tim)
+{
+ Tm *ct;
+ long t, *p;
+ int dlflag;
+
+ if(timezone.stname[0] == 0)
+ readtimezone();
+ t = tim + timezone.stdiff;
+ dlflag = 0;
+ for(p = timezone.dlpairs; *p; p += 2)
+ if(t >= p[0])
+ if(t < p[1]) {
+ t = tim + timezone.dldiff;
+ dlflag++;
+ break;
+ }
+ ct = gmtime(t);
+ if(dlflag){
+ strcpy(ct->zone, timezone.dlname);
+ ct->tzoff = timezone.dldiff;
+ } else {
+ strcpy(ct->zone, timezone.stname);
+ ct->tzoff = timezone.stdiff;
+ }
+ return ct;
+}
+
+Tm*
+gmtime(long tim)
+{
+ int d0, d1;
+ long hms, day;
+ static Tm xtime;
+
+ /*
+ * break initial number into days
+ */
+ hms = tim % 86400L;
+ day = tim / 86400L;
+ if(hms < 0) {
+ hms += 86400L;
+ day -= 1;
+ }
+
+ /*
+ * generate hours:minutes:seconds
+ */
+ xtime.sec = hms % 60;
+ d1 = hms / 60;
+ xtime.min = d1 % 60;
+ d1 /= 60;
+ xtime.hour = d1;
+
+ /*
+ * day is the day number.
+ * generate day of the week.
+ * The addend is 4 mod 7 (1/1/1970 was Thursday)
+ */
+
+ xtime.wday = (day + 7340036L) % 7;
+
+ /*
+ * year number
+ */
+ if(day >= 0)
+ for(d1 = 1970; day >= dysize(d1); d1++)
+ day -= dysize(d1);
+ else
+ for (d1 = 1970; day < 0; d1--)
+ day += dysize(d1-1);
+ xtime.year = d1-1900;
+ xtime.yday = d0 = day;
+
+ /*
+ * generate month
+ */
+
+ if(dysize(d1) == 366)
+ dmsize[1] = 29;
+ for(d1 = 0; d0 >= dmsize[d1]; d1++)
+ d0 -= dmsize[d1];
+ dmsize[1] = 28;
+ xtime.mday = d0 + 1;
+ xtime.mon = d1;
+ strcpy(xtime.zone, "GMT");
+ return &xtime;
+}
+
+char*
+asctime(Tm *t)
+{
+ char *ncp;
+ static char cbuf[30];
+
+ strcpy(cbuf, "Thu Jan 01 00:00:00 GMT 1970\n");
+ ncp = &"SunMonTueWedThuFriSat"[t->wday*3];
+ cbuf[0] = *ncp++;
+ cbuf[1] = *ncp++;
+ cbuf[2] = *ncp;
+ ncp = &"JanFebMarAprMayJunJulAugSepOctNovDec"[t->mon*3];
+ cbuf[4] = *ncp++;
+ cbuf[5] = *ncp++;
+ cbuf[6] = *ncp;
+ ct_numb(cbuf+8, t->mday);
+ ct_numb(cbuf+11, t->hour+100);
+ ct_numb(cbuf+14, t->min+100);
+ ct_numb(cbuf+17, t->sec+100);
+ ncp = t->zone;
+ cbuf[20] = *ncp++;
+ cbuf[21] = *ncp++;
+ cbuf[22] = *ncp;
+ if(t->year >= 100) {
+ cbuf[24] = '2';
+ cbuf[25] = '0';
+ }
+ ct_numb(cbuf+26, t->year+100);
+ return cbuf;
+}
+
+static
+dysize(int y)
+{
+
+ if(y%4 == 0 && (y%100 != 0 || y%400 == 0))
+ return 366;
+ return 365;
+}
+
+static
+void
+ct_numb(char *cp, int n)
+{
+
+ cp[0] = ' ';
+ if(n >= 10)
+ cp[0] = (n/10)%10 + '0';
+ cp[1] = n%10 + '0';
+}
+
+static
+void
+readtimezone(void)
+{
+ char buf[TZSIZE*11+30], *p;
+ int i;
+
+ memset(buf, 0, sizeof(buf));
+ i = open("/env/timezone", 0);
+ if(i < 0)
+ goto error;
+ if(read(i, buf, sizeof(buf)) >= sizeof(buf)){
+ close(i);
+ goto error;
+ }
+ close(i);
+ p = buf;
+ if(rd_name(&p, timezone.stname))
+ goto error;
+ if(rd_long(&p, &timezone.stdiff))
+ goto error;
+ if(rd_name(&p, timezone.dlname))
+ goto error;
+ if(rd_long(&p, &timezone.dldiff))
+ goto error;
+ for(i=0; i<TZSIZE; i++) {
+ if(rd_long(&p, &timezone.dlpairs[i]))
+ goto error;
+ if(timezone.dlpairs[i] == 0)
+ return;
+ }
+
+error:
+ timezone.stdiff = 0;
+ strcpy(timezone.stname, "GMT");
+ timezone.dlpairs[0] = 0;
+}
+
+static
+rd_name(char **f, char *p)
+{
+ int c, i;
+
+ for(;;) {
+ c = *(*f)++;
+ if(c != ' ' && c != '\n')
+ break;
+ }
+ for(i=0; i<3; i++) {
+ if(c == ' ' || c == '\n')
+ return 1;
+ *p++ = c;
+ c = *(*f)++;
+ }
+ if(c != ' ' && c != '\n')
+ return 1;
+ *p = 0;
+ return 0;
+}
+
+static
+rd_long(char **f, long *p)
+{
+ int c, s;
+ long l;
+
+ s = 0;
+ for(;;) {
+ c = *(*f)++;
+ if(c == '-') {
+ s++;
+ continue;
+ }
+ if(c != ' ' && c != '\n')
+ break;
+ }
+ if(c == 0) {
+ *p = 0;
+ return 0;
+ }
+ l = 0;
+ for(;;) {
+ if(c == ' ' || c == '\n')
+ break;
+ if(c < '0' || c > '9')
+ return 1;
+ l = l*10 + c-'0';
+ c = *(*f)++;
+ }
+ if(s)
+ l = -l;
+ *p = l;
+ return 0;
+}
diff --git a/sys/src/libc/9sys/dial.c b/sys/src/libc/9sys/dial.c
new file mode 100755
index 000000000..74fc6e6ae
--- /dev/null
+++ b/sys/src/libc/9sys/dial.c
@@ -0,0 +1,492 @@
+/*
+ * dial - connect to a service (parallel version)
+ */
+#include <u.h>
+#include <libc.h>
+
+typedef struct Conn Conn;
+typedef struct Dest Dest;
+typedef struct DS DS;
+
+enum
+{
+ Maxstring = 128,
+ Maxpath = 256,
+
+ Maxcsreply = 64*80, /* this is probably overly generous */
+ /*
+ * this should be a plausible slight overestimate for non-interactive
+ * use even if it's ridiculously long for interactive use.
+ */
+ Maxconnms = 20*60*1000, /* 20 minutes */
+};
+
+struct DS {
+ /* dist string */
+ char buf[Maxstring];
+ char *netdir;
+ char *proto;
+ char *rem;
+
+ /* other args */
+ char *local;
+ char *dir;
+ int *cfdp;
+};
+
+/*
+ * malloc these; they need to be writable by this proc & all children.
+ * the stack is private to each proc, and static allocation in the data
+ * segment would not permit concurrent dials within a multi-process program.
+ */
+struct Conn {
+ int pid;
+ int dead;
+
+ int dfd;
+ int cfd;
+ char dir[NETPATHLEN];
+ char err[ERRMAX];
+};
+struct Dest {
+ Conn *conn; /* allocated array */
+ Conn *connend;
+ int nkid;
+
+ QLock winlck;
+ int winner; /* index into conn[] */
+
+ char *nextaddr;
+ char addrlist[Maxcsreply];
+};
+
+static int call(char*, char*, DS*, Dest*, Conn*);
+static int csdial(DS*);
+static void _dial_string_parse(char*, DS*);
+
+
+/*
+ * the dialstring is of the form '[/net/]proto!dest'
+ */
+static int
+dialimpl(char *dest, char *local, char *dir, int *cfdp)
+{
+ DS ds;
+ int rv;
+ char err[ERRMAX], alterr[ERRMAX];
+
+ ds.local = local;
+ ds.dir = dir;
+ ds.cfdp = cfdp;
+
+ _dial_string_parse(dest, &ds);
+ if(ds.netdir)
+ return csdial(&ds);
+
+ ds.netdir = "/net";
+ rv = csdial(&ds);
+ if(rv >= 0)
+ return rv;
+ err[0] = '\0';
+ errstr(err, sizeof err);
+ if(strstr(err, "refused") != 0){
+ werrstr("%s", err);
+ return rv;
+ }
+ ds.netdir = "/net.alt";
+ rv = csdial(&ds);
+ if(rv >= 0)
+ return rv;
+
+ alterr[0] = 0;
+ errstr(alterr, sizeof alterr);
+ if(strstr(alterr, "translate") || strstr(alterr, "does not exist"))
+ werrstr("%s", err);
+ else
+ werrstr("%s", alterr);
+ return rv;
+}
+
+/*
+ * the thread library can't cope with rfork(RFMEM|RFPROC),
+ * so it must override this with a private version of dial.
+ */
+int (*_dial)(char *, char *, char *, int *) = dialimpl;
+
+int
+dial(char *dest, char *local, char *dir, int *cfdp)
+{
+ return (*_dial)(dest, local, dir, cfdp);
+}
+
+static int
+connsalloc(Dest *dp, int addrs)
+{
+ free(dp->conn);
+ dp->connend = nil;
+ assert(addrs > 0);
+
+ dp->conn = mallocz(addrs * sizeof *dp->conn, 1);
+ if(dp->conn == nil)
+ return -1;
+ dp->connend = dp->conn + addrs;
+ return 0;
+}
+
+static void
+freedest(Dest *dp)
+{
+ if (dp != nil) {
+ free(dp->conn);
+ free(dp);
+ }
+}
+
+static void
+closeopenfd(int *fdp)
+{
+ if (*fdp > 0) {
+ close(*fdp);
+ *fdp = -1;
+ }
+}
+
+static void
+notedeath(Dest *dp, char *exitsts)
+{
+ int i, n, pid;
+ char *fields[5]; /* pid + 3 times + error */
+ Conn *conn;
+
+ for (i = 0; i < nelem(fields); i++)
+ fields[i] = "";
+ n = tokenize(exitsts, fields, nelem(fields));
+ if (n < 4)
+ return;
+ pid = atoi(fields[0]);
+ if (pid <= 0)
+ return;
+ for (conn = dp->conn; conn < dp->connend; conn++)
+ if (conn->pid == pid && !conn->dead) { /* it's one we know? */
+ if (conn - dp->conn != dp->winner) {
+ closeopenfd(&conn->dfd);
+ closeopenfd(&conn->cfd);
+ }
+ strncpy(conn->err, fields[4], sizeof conn->err);
+ conn->dead = 1;
+ return;
+ }
+ /* not a proc that we forked */
+}
+
+static int
+outstandingprocs(Dest *dp)
+{
+ Conn *conn;
+
+ for (conn = dp->conn; conn < dp->connend; conn++)
+ if (!conn->dead)
+ return 1;
+ return 0;
+}
+
+static int
+reap(Dest *dp)
+{
+ char exitsts[2*ERRMAX];
+
+ if (outstandingprocs(dp) && await(exitsts, sizeof exitsts) >= 0) {
+ notedeath(dp, exitsts);
+ return 0;
+ }
+ return -1;
+}
+
+static int
+fillinds(DS *ds, Dest *dp)
+{
+ Conn *conn;
+
+ if (dp->winner < 0)
+ return -1;
+ conn = &dp->conn[dp->winner];
+ if (ds->cfdp)
+ *ds->cfdp = conn->cfd;
+ if (ds->dir)
+ strncpy(ds->dir, conn->dir, NETPATHLEN);
+ return conn->dfd;
+}
+
+static int
+connectwait(Dest *dp, char *besterr)
+{
+ Conn *conn;
+
+ /* wait for a winner or all attempts to time out */
+ while (dp->winner < 0 && reap(dp) >= 0)
+ ;
+
+ /* kill all of our still-live kids & reap them */
+ for (conn = dp->conn; conn < dp->connend; conn++)
+ if (!conn->dead)
+ postnote(PNPROC, conn->pid, "die");
+ while (reap(dp) >= 0)
+ ;
+
+ /* rummage about and report some error string */
+ for (conn = dp->conn; conn < dp->connend; conn++)
+ if (conn - dp->conn != dp->winner && conn->dead &&
+ conn->err[0]) {
+ strncpy(besterr, conn->err, ERRMAX);
+ break;
+ }
+ return dp->winner;
+}
+
+static int
+parsecs(Dest *dp, char **clonep, char **destp)
+{
+ char *dest, *p;
+
+ dest = strchr(dp->nextaddr, ' ');
+ if(dest == nil)
+ return -1;
+ *dest++ = '\0';
+ p = strchr(dest, '\n');
+ if(p == nil)
+ return -1;
+ *p++ = '\0';
+ *clonep = dp->nextaddr;
+ *destp = dest;
+ dp->nextaddr = p; /* advance to next line */
+ return 0;
+}
+
+static void
+pickuperr(char *besterr, char *err)
+{
+ err[0] = '\0';
+ errstr(err, ERRMAX);
+ if(strstr(err, "does not exist") == 0)
+ strcpy(besterr, err);
+}
+
+/*
+ * try all addresses in parallel and take the first one that answers;
+ * this helps when systems have ip v4 and v6 addresses but are
+ * only reachable from here on one (or some) of them.
+ */
+static int
+dialmulti(DS *ds, Dest *dp)
+{
+ int rv, kid, kidme;
+ char *clone, *dest;
+ char err[ERRMAX], besterr[ERRMAX];
+
+ dp->winner = -1;
+ dp->nkid = 0;
+ while(dp->winner < 0 && *dp->nextaddr != '\0' &&
+ parsecs(dp, &clone, &dest) >= 0) {
+ kidme = dp->nkid++; /* make private copy on stack */
+ kid = rfork(RFPROC|RFMEM); /* spin off a call attempt */
+ if (kid < 0)
+ --dp->nkid;
+ else if (kid == 0) {
+ alarm(Maxconnms);
+ *besterr = '\0';
+ rv = call(clone, dest, ds, dp, &dp->conn[kidme]);
+ if(rv < 0)
+ pickuperr(besterr, err);
+ _exits(besterr); /* avoid atexit callbacks */
+ }
+ }
+ rv = connectwait(dp, besterr);
+ if(rv < 0 && *besterr)
+ werrstr("%s", besterr);
+ else
+ werrstr("%s", err);
+ return rv;
+}
+
+static int
+csdial(DS *ds)
+{
+ int n, fd, rv, addrs, bleft;
+ char c;
+ char *addrp, *clone2, *dest;
+ char buf[Maxstring], clone[Maxpath], err[ERRMAX], besterr[ERRMAX];
+ Dest *dp;
+
+ dp = mallocz(sizeof *dp, 1);
+ if(dp == nil)
+ return -1;
+ dp->winner = -1;
+ if (connsalloc(dp, 1) < 0) { /* room for a single conn. */
+ freedest(dp);
+ return -1;
+ }
+
+ /*
+ * open connection server
+ */
+ snprint(buf, sizeof(buf), "%s/cs", ds->netdir);
+ fd = open(buf, ORDWR);
+ if(fd < 0){
+ /* no connection server, don't translate */
+ snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto);
+ rv = call(clone, ds->rem, ds, dp, &dp->conn[0]);
+ fillinds(ds, dp);
+ freedest(dp);
+ return rv;
+ }
+
+ /*
+ * ask connection server to translate
+ */
+ snprint(buf, sizeof(buf), "%s!%s", ds->proto, ds->rem);
+ if(write(fd, buf, strlen(buf)) < 0){
+ close(fd);
+ freedest(dp);
+ return -1;
+ }
+
+ /*
+ * read all addresses from the connection server.
+ */
+ seek(fd, 0, 0);
+ addrs = 0;
+ addrp = dp->nextaddr = dp->addrlist;
+ bleft = sizeof dp->addrlist - 2; /* 2 is room for \n\0 */
+ while(bleft > 0 && (n = read(fd, addrp, bleft)) > 0) {
+ if (addrp[n-1] != '\n')
+ addrp[n++] = '\n';
+ addrs++;
+ addrp += n;
+ bleft -= n;
+ }
+ /*
+ * if we haven't read all of cs's output, assume the last line might
+ * have been truncated and ignore it. we really don't expect this
+ * to happen.
+ */
+ if (addrs > 0 && bleft <= 0 && read(fd, &c, 1) == 1)
+ addrs--;
+ close(fd);
+
+ *besterr = 0;
+ rv = -1; /* pessimistic default */
+ if (addrs == 0)
+ werrstr("no address to dial");
+ else if (addrs == 1) {
+ /* common case: dial one address without forking */
+ if (parsecs(dp, &clone2, &dest) >= 0 &&
+ (rv = call(clone2, dest, ds, dp, &dp->conn[0])) < 0) {
+ pickuperr(besterr, err);
+ werrstr("%s", besterr);
+ }
+ } else if (connsalloc(dp, addrs) >= 0)
+ rv = dialmulti(ds, dp);
+
+ /* fill in results */
+ if (rv >= 0 && dp->winner >= 0)
+ rv = fillinds(ds, dp);
+
+ freedest(dp);
+ return rv;
+}
+
+static int
+call(char *clone, char *dest, DS *ds, Dest *dp, Conn *conn)
+{
+ int fd, cfd, n;
+ char cname[Maxpath], name[Maxpath], data[Maxpath], *p;
+
+ /* because cs is in a different name space, replace the mount point */
+ if(*clone == '/'){
+ p = strchr(clone+1, '/');
+ if(p == nil)
+ p = clone;
+ else
+ p++;
+ } else
+ p = clone;
+ snprint(cname, sizeof cname, "%s/%s", ds->netdir, p);
+
+ conn->pid = getpid();
+ conn->cfd = cfd = open(cname, ORDWR);
+ if(cfd < 0)
+ return -1;
+
+ /* get directory name */
+ n = read(cfd, name, sizeof(name)-1);
+ if(n < 0){
+ closeopenfd(&conn->cfd);
+ return -1;
+ }
+ name[n] = 0;
+ for(p = name; *p == ' '; p++)
+ ;
+ snprint(name, sizeof(name), "%ld", strtoul(p, 0, 0));
+ p = strrchr(cname, '/');
+ *p = 0;
+ if(ds->dir)
+ snprint(conn->dir, NETPATHLEN, "%s/%s", cname, name);
+ snprint(data, sizeof(data), "%s/%s/data", cname, name);
+
+ /* connect */
+ if(ds->local)
+ snprint(name, sizeof(name), "connect %s %s", dest, ds->local);
+ else
+ snprint(name, sizeof(name), "connect %s", dest);
+ if(write(cfd, name, strlen(name)) < 0){
+ closeopenfd(&conn->cfd);
+ return -1;
+ }
+
+ /* open data connection */
+ conn->dfd = fd = open(data, ORDWR);
+ if(fd < 0){
+ closeopenfd(&conn->cfd);
+ return -1;
+ }
+ if(ds->cfdp == nil)
+ closeopenfd(&conn->cfd);
+
+ qlock(&dp->winlck);
+ if (dp->winner < 0 && conn < dp->connend)
+ dp->winner = conn - dp->conn;
+ qunlock(&dp->winlck);
+ return fd;
+}
+
+/*
+ * parse a dial string
+ */
+static void
+_dial_string_parse(char *str, DS *ds)
+{
+ char *p, *p2;
+
+ strncpy(ds->buf, str, Maxstring);
+ ds->buf[Maxstring-1] = 0;
+
+ p = strchr(ds->buf, '!');
+ if(p == 0) {
+ ds->netdir = 0;
+ ds->proto = "net";
+ ds->rem = ds->buf;
+ } else {
+ if(*ds->buf != '/' && *ds->buf != '#'){
+ ds->netdir = 0;
+ ds->proto = ds->buf;
+ } else {
+ for(p2 = p; *p2 != '/'; p2--)
+ ;
+ *p2++ = 0;
+ ds->netdir = ds->buf;
+ ds->proto = p2;
+ }
+ *p = 0;
+ ds->rem = p + 1;
+ }
+}
diff --git a/sys/src/libc/9sys/dirfstat.c b/sys/src/libc/9sys/dirfstat.c
new file mode 100755
index 000000000..0534a2278
--- /dev/null
+++ b/sys/src/libc/9sys/dirfstat.c
@@ -0,0 +1,37 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+enum
+{
+ DIRSIZE = STATFIXLEN + 16 * 4 /* enough for encoded stat buf + some reasonable strings */
+};
+
+Dir*
+dirfstat(int fd)
+{
+ Dir *d;
+ uchar *buf;
+ int n, nd, i;
+
+ nd = DIRSIZE;
+ for(i=0; i<2; i++){ /* should work by the second try */
+ d = malloc(sizeof(Dir) + BIT16SZ + nd);
+ if(d == nil)
+ return nil;
+ buf = (uchar*)&d[1];
+ n = fstat(fd, buf, BIT16SZ+nd);
+ if(n < BIT16SZ){
+ free(d);
+ return nil;
+ }
+ nd = GBIT16(buf); /* upper bound on size of Dir + strings */
+ if(nd <= n){
+ convM2D(buf, n, d, (char*)&d[1]);
+ return d;
+ }
+ /* else sizeof(Dir)+BIT16SZ+nd is plenty */
+ free(d);
+ }
+ return nil;
+}
diff --git a/sys/src/libc/9sys/dirfwstat.c b/sys/src/libc/9sys/dirfwstat.c
new file mode 100755
index 000000000..85803ff4c
--- /dev/null
+++ b/sys/src/libc/9sys/dirfwstat.c
@@ -0,0 +1,19 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+int
+dirfwstat(int fd, Dir *d)
+{
+ uchar *buf;
+ int r;
+
+ r = sizeD2M(d);
+ buf = malloc(r);
+ if(buf == nil)
+ return -1;
+ convD2M(d, buf, r);
+ r = fwstat(fd, buf, r);
+ free(buf);
+ return r;
+}
diff --git a/sys/src/libc/9sys/dirmodefmt.c b/sys/src/libc/9sys/dirmodefmt.c
new file mode 100755
index 000000000..82eb5a308
--- /dev/null
+++ b/sys/src/libc/9sys/dirmodefmt.c
@@ -0,0 +1,48 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+static char *modes[] =
+{
+ "---",
+ "--x",
+ "-w-",
+ "-wx",
+ "r--",
+ "r-x",
+ "rw-",
+ "rwx",
+};
+
+static void
+rwx(long m, char *s)
+{
+ strncpy(s, modes[m], 3);
+}
+
+int
+dirmodefmt(Fmt *f)
+{
+ static char buf[16];
+ ulong m;
+
+ m = va_arg(f->args, ulong);
+
+ if(m & DMDIR)
+ buf[0]='d';
+ else if(m & DMAPPEND)
+ buf[0]='a';
+ else if(m & DMAUTH)
+ buf[0]='A';
+ else
+ buf[0]='-';
+ if(m & DMEXCL)
+ buf[1]='l';
+ else
+ buf[1]='-';
+ rwx((m>>6)&7, buf+2);
+ rwx((m>>3)&7, buf+5);
+ rwx((m>>0)&7, buf+8);
+ buf[11] = 0;
+ return fmtstrcpy(f, buf);
+}
diff --git a/sys/src/libc/9sys/dirread.c b/sys/src/libc/9sys/dirread.c
new file mode 100755
index 000000000..0efd2f954
--- /dev/null
+++ b/sys/src/libc/9sys/dirread.c
@@ -0,0 +1,97 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+static
+long
+dirpackage(uchar *buf, long ts, Dir **d)
+{
+ char *s;
+ long ss, i, n, nn, m;
+
+ *d = nil;
+ if(ts <= 0)
+ return 0;
+
+ /*
+ * first find number of all stats, check they look like stats, & size all associated strings
+ */
+ ss = 0;
+ n = 0;
+ for(i = 0; i < ts; i += m){
+ m = BIT16SZ + GBIT16(&buf[i]);
+ if(statcheck(&buf[i], m) < 0)
+ break;
+ ss += m;
+ n++;
+ }
+
+ if(i != ts)
+ return -1;
+
+ *d = malloc(n * sizeof(Dir) + ss);
+ if(*d == nil)
+ return -1;
+
+ /*
+ * then convert all buffers
+ */
+ s = (char*)*d + n * sizeof(Dir);
+ nn = 0;
+ for(i = 0; i < ts; i += m){
+ m = BIT16SZ + GBIT16((uchar*)&buf[i]);
+ if(nn >= n || convM2D(&buf[i], m, *d + nn, s) != m){
+ free(*d);
+ *d = nil;
+ return -1;
+ }
+ nn++;
+ s += m;
+ }
+
+ return nn;
+}
+
+long
+dirread(int fd, Dir **d)
+{
+ uchar *buf;
+ long ts;
+
+ buf = malloc(DIRMAX);
+ if(buf == nil)
+ return -1;
+ ts = read(fd, buf, DIRMAX);
+ if(ts >= 0)
+ ts = dirpackage(buf, ts, d);
+ free(buf);
+ return ts;
+}
+
+long
+dirreadall(int fd, Dir **d)
+{
+ uchar *buf, *nbuf;
+ long n, ts;
+
+ buf = nil;
+ ts = 0;
+ for(;;){
+ nbuf = realloc(buf, ts+DIRMAX);
+ if(nbuf == nil){
+ free(buf);
+ return -1;
+ }
+ buf = nbuf;
+ n = read(fd, buf+ts, DIRMAX);
+ if(n <= 0)
+ break;
+ ts += n;
+ }
+ if(ts >= 0)
+ ts = dirpackage(buf, ts, d);
+ free(buf);
+ if(ts == 0 && n < 0)
+ return -1;
+ return ts;
+}
diff --git a/sys/src/libc/9sys/dirstat.c b/sys/src/libc/9sys/dirstat.c
new file mode 100755
index 000000000..eb5171d2a
--- /dev/null
+++ b/sys/src/libc/9sys/dirstat.c
@@ -0,0 +1,37 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+enum
+{
+ DIRSIZE = STATFIXLEN + 16 * 4 /* enough for encoded stat buf + some reasonable strings */
+};
+
+Dir*
+dirstat(char *name)
+{
+ Dir *d;
+ uchar *buf;
+ int n, nd, i;
+
+ nd = DIRSIZE;
+ for(i=0; i<2; i++){ /* should work by the second try */
+ d = malloc(sizeof(Dir) + BIT16SZ + nd);
+ if(d == nil)
+ return nil;
+ buf = (uchar*)&d[1];
+ n = stat(name, buf, BIT16SZ+nd);
+ if(n < BIT16SZ){
+ free(d);
+ return nil;
+ }
+ nd = GBIT16((uchar*)buf); /* upper bound on size of Dir + strings */
+ if(nd <= n){
+ convM2D(buf, n, d, (char*)&d[1]);
+ return d;
+ }
+ /* else sizeof(Dir)+BIT16SZ+nd is plenty */
+ free(d);
+ }
+ return nil;
+}
diff --git a/sys/src/libc/9sys/dirwstat.c b/sys/src/libc/9sys/dirwstat.c
new file mode 100755
index 000000000..cb62e3fc4
--- /dev/null
+++ b/sys/src/libc/9sys/dirwstat.c
@@ -0,0 +1,19 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+int
+dirwstat(char *name, Dir *d)
+{
+ uchar *buf;
+ int r;
+
+ r = sizeD2M(d);
+ buf = malloc(r);
+ if(buf == nil)
+ return -1;
+ convD2M(d, buf, r);
+ r = wstat(name, buf, r);
+ free(buf);
+ return r;
+}
diff --git a/sys/src/libc/9sys/fcallfmt.c b/sys/src/libc/9sys/fcallfmt.c
new file mode 100755
index 000000000..c5a8af614
--- /dev/null
+++ b/sys/src/libc/9sys/fcallfmt.c
@@ -0,0 +1,234 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+static uint dumpsome(char*, char*, char*, long);
+static void fdirconv(char*, char*, Dir*);
+static char *qidtype(char*, uchar);
+
+#define QIDFMT "(%.16llux %lud %s)"
+
+int
+fcallfmt(Fmt *fmt)
+{
+ Fcall *f;
+ int fid, type, tag, i;
+ char buf[512], tmp[200];
+ char *p, *e;
+ Dir *d;
+ Qid *q;
+
+ e = buf+sizeof(buf);
+ f = va_arg(fmt->args, Fcall*);
+ type = f->type;
+ fid = f->fid;
+ tag = f->tag;
+ switch(type){
+ case Tversion: /* 100 */
+ seprint(buf, e, "Tversion tag %ud msize %ud version '%s'", tag, f->msize, f->version);
+ break;
+ case Rversion:
+ seprint(buf, e, "Rversion tag %ud msize %ud version '%s'", tag, f->msize, f->version);
+ break;
+ case Tauth: /* 102 */
+ seprint(buf, e, "Tauth tag %ud afid %d uname %s aname %s", tag,
+ f->afid, f->uname, f->aname);
+ break;
+ case Rauth:
+ seprint(buf, e, "Rauth tag %ud qid " QIDFMT, tag,
+ f->aqid.path, f->aqid.vers, qidtype(tmp, f->aqid.type));
+ break;
+ case Tattach: /* 104 */
+ seprint(buf, e, "Tattach tag %ud fid %d afid %d uname %s aname %s", tag,
+ fid, f->afid, f->uname, f->aname);
+ break;
+ case Rattach:
+ seprint(buf, e, "Rattach tag %ud qid " QIDFMT, tag,
+ f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type));
+ break;
+ case Rerror: /* 107; 106 (Terror) illegal */
+ seprint(buf, e, "Rerror tag %ud ename %s", tag, f->ename);
+ break;
+ case Tflush: /* 108 */
+ seprint(buf, e, "Tflush tag %ud oldtag %ud", tag, f->oldtag);
+ break;
+ case Rflush:
+ seprint(buf, e, "Rflush tag %ud", tag);
+ break;
+ case Twalk: /* 110 */
+ p = seprint(buf, e, "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++)
+ p = seprint(p, e, "%d:%s ", i, f->wname[i]);
+ break;
+ case Rwalk:
+ p = seprint(buf, e, "Rwalk tag %ud nwqid %ud ", tag, f->nwqid);
+ if(f->nwqid <= MAXWELEM)
+ for(i=0; i<f->nwqid; i++){
+ q = &f->wqid[i];
+ p = seprint(p, e, "%d:" QIDFMT " ", i,
+ q->path, q->vers, qidtype(tmp, q->type));
+ }
+ break;
+ case Topen: /* 112 */
+ 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,
+ f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type), f->iounit);
+ 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;
+ case Rcreate:
+ seprint(buf, e, "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 */
+ seprint(buf, e, "Tread tag %ud fid %d offset %lld count %ud",
+ tag, fid, f->offset, f->count);
+ break;
+ case Rread:
+ p = seprint(buf, e, "Rread tag %ud count %ud ", tag, f->count);
+ dumpsome(p, e, f->data, f->count);
+ break;
+ case Twrite: /* 118 */
+ p = seprint(buf, e, "Twrite tag %ud fid %d offset %lld count %ud ",
+ tag, fid, f->offset, f->count);
+ dumpsome(p, e, f->data, f->count);
+ break;
+ case Rwrite:
+ seprint(buf, e, "Rwrite tag %ud count %ud", tag, f->count);
+ break;
+ case Tclunk: /* 120 */
+ seprint(buf, e, "Tclunk tag %ud fid %ud", tag, fid);
+ break;
+ case Rclunk:
+ seprint(buf, e, "Rclunk tag %ud", tag);
+ break;
+ case Tremove: /* 122 */
+ seprint(buf, e, "Tremove tag %ud fid %ud", tag, fid);
+ break;
+ case Rremove:
+ seprint(buf, e, "Rremove tag %ud", tag);
+ break;
+ case Tstat: /* 124 */
+ seprint(buf, e, "Tstat tag %ud fid %ud", tag, fid);
+ break;
+ case Rstat:
+ p = seprint(buf, e, "Rstat tag %ud ", tag);
+ if(f->nstat > sizeof tmp)
+ seprint(p, e, " stat(%d bytes)", f->nstat);
+ else{
+ d = (Dir*)tmp;
+ convM2D(f->stat, f->nstat, d, (char*)(d+1));
+ seprint(p, e, " stat ");
+ fdirconv(p+6, e, d);
+ }
+ break;
+ case Twstat: /* 126 */
+ p = seprint(buf, e, "Twstat tag %ud fid %ud", tag, fid);
+ if(f->nstat > sizeof tmp)
+ seprint(p, e, " stat(%d bytes)", f->nstat);
+ else{
+ d = (Dir*)tmp;
+ convM2D(f->stat, f->nstat, d, (char*)(d+1));
+ seprint(p, e, " stat ");
+ fdirconv(p+6, e, d);
+ }
+ break;
+ case Rwstat:
+ seprint(buf, e, "Rwstat tag %ud", tag);
+ break;
+ default:
+ seprint(buf, e, "unknown type %d", type);
+ }
+ return fmtstrcpy(fmt, buf);
+}
+
+static char*
+qidtype(char *s, uchar t)
+{
+ char *p;
+
+ p = s;
+ if(t & QTDIR)
+ *p++ = 'd';
+ if(t & QTAPPEND)
+ *p++ = 'a';
+ if(t & QTEXCL)
+ *p++ = 'l';
+ if(t & QTAUTH)
+ *p++ = 'A';
+ *p = '\0';
+ return s;
+}
+
+int
+dirfmt(Fmt *fmt)
+{
+ char buf[160];
+
+ fdirconv(buf, buf+sizeof buf, va_arg(fmt->args, Dir*));
+ return fmtstrcpy(fmt, buf);
+}
+
+static void
+fdirconv(char *buf, char *e, Dir *d)
+{
+ char tmp[16];
+
+ seprint(buf, e, "'%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 ans, as a string if they are all printable,
+ * else as a series of hex bytes
+ */
+#define DUMPL 64
+
+static uint
+dumpsome(char *ans, char *e, char *buf, long count)
+{
+ int i, printable;
+ char *p;
+
+ if(buf == nil){
+ seprint(ans, e, "<no data>");
+ return strlen(ans);
+ }
+ printable = 1;
+ if(count > DUMPL)
+ count = DUMPL;
+ for(i=0; i<count && printable; i++)
+ if((buf[i]<32 && buf[i] !='\n' && buf[i] !='\t') || (uchar)buf[i]>127)
+ printable = 0;
+ p = ans;
+ *p++ = '\'';
+ if(printable){
+ if(count > e-p-2)
+ count = e-p-2;
+ memmove(p, buf, count);
+ p += count;
+ }else{
+ if(2*count > e-p-2)
+ count = (e-p-2)/2;
+ for(i=0; i<count; i++){
+ if(i>0 && i%4==0)
+ *p++ = ' ';
+ sprint(p, "%2.2ux", (uchar)buf[i]);
+ p += 2;
+ }
+ }
+ *p++ = '\'';
+ *p = 0;
+ return p - ans;
+}
diff --git a/sys/src/libc/9sys/fork.c b/sys/src/libc/9sys/fork.c
new file mode 100755
index 000000000..46296ed5d
--- /dev/null
+++ b/sys/src/libc/9sys/fork.c
@@ -0,0 +1,8 @@
+#include <u.h>
+#include <libc.h>
+
+int
+fork(void)
+{
+ return rfork(RFPROC|RFFDG|RFREND);
+}
diff --git a/sys/src/libc/9sys/getenv.c b/sys/src/libc/9sys/getenv.c
new file mode 100755
index 000000000..146b35b62
--- /dev/null
+++ b/sys/src/libc/9sys/getenv.c
@@ -0,0 +1,36 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+getenv(char *name)
+{
+ int r, f;
+ long s;
+ char *ans;
+ char *p, *ep, ename[100];
+
+ if(strchr(name, '/') != nil)
+ return nil;
+ snprint(ename, sizeof ename, "/env/%s", name);
+ if(strcmp(ename+5, name) != 0)
+ return nil;
+ f = open(ename, OREAD);
+ if(f < 0)
+ return 0;
+ s = seek(f, 0, 2);
+ ans = malloc(s+1);
+ if(ans) {
+ setmalloctag(ans, getcallerpc(&name));
+ seek(f, 0, 0);
+ r = read(f, ans, s);
+ if(r >= 0) {
+ ep = ans + s - 1;
+ for(p = ans; p < ep; p++)
+ if(*p == '\0')
+ *p = ' ';
+ ans[s] = '\0';
+ }
+ }
+ close(f);
+ return ans;
+}
diff --git a/sys/src/libc/9sys/getnetconninfo.c b/sys/src/libc/9sys/getnetconninfo.c
new file mode 100755
index 000000000..8dbb95f89
--- /dev/null
+++ b/sys/src/libc/9sys/getnetconninfo.c
@@ -0,0 +1,133 @@
+#include <u.h>
+#include <libc.h>
+
+static char *unknown = "???";
+
+static void
+getendpoint(char *dir, char *file, char **sysp, char **servp)
+{
+ int fd, n;
+ char buf[128];
+ char *sys, *serv;
+
+ sys = serv = 0;
+
+ snprint(buf, sizeof buf, "%s/%s", dir, file);
+ fd = open(buf, OREAD);
+ if(fd >= 0){
+ n = read(fd, buf, sizeof(buf)-1);
+ if(n>0){
+ buf[n-1] = 0;
+ serv = strchr(buf, '!');
+ if(serv){
+ *serv++ = 0;
+ serv = strdup(serv);
+ }
+ sys = strdup(buf);
+ }
+ close(fd);
+ }
+ if(serv == 0)
+ serv = unknown;
+ if(sys == 0)
+ sys = unknown;
+ *servp = serv;
+ *sysp = sys;
+}
+
+NetConnInfo*
+getnetconninfo(char *dir, int fd)
+{
+ NetConnInfo *nci;
+ char *cp;
+ Dir *d;
+ char spec[10];
+ char path[128];
+ char netname[128], *p;
+
+ /* get a directory address via fd */
+ if(dir == nil || *dir == 0){
+ if(fd2path(fd, path, sizeof(path)) < 0)
+ return nil;
+ cp = strrchr(path, '/');
+ if(cp == nil)
+ return nil;
+ *cp = 0;
+ dir = path;
+ }
+
+ nci = mallocz(sizeof *nci, 1);
+ if(nci == nil)
+ return nil;
+
+ /* copy connection directory */
+ nci->dir = strdup(dir);
+ if(nci->dir == nil)
+ goto err;
+
+ /* get netroot */
+ nci->root = strdup(dir);
+ if(nci->root == nil)
+ goto err;
+ cp = strchr(nci->root+1, '/');
+ if(cp == nil)
+ goto err;
+ *cp = 0;
+
+ /* figure out bind spec */
+ d = dirstat(nci->dir);
+ if(d != nil){
+ sprint(spec, "#%C%d", d->type, d->dev);
+ nci->spec = strdup(spec);
+ }
+ if(nci->spec == nil)
+ nci->spec = unknown;
+ free(d);
+
+ /* get the two end points */
+ getendpoint(nci->dir, "local", &nci->lsys, &nci->lserv);
+ if(nci->lsys == nil || nci->lserv == nil)
+ goto err;
+ getendpoint(nci->dir, "remote", &nci->rsys, &nci->rserv);
+ if(nci->rsys == nil || nci->rserv == nil)
+ goto err;
+
+ strecpy(netname, netname+sizeof netname, nci->dir);
+ if((p = strrchr(netname, '/')) != nil)
+ *p = 0;
+ if(strncmp(netname, "/net/", 5) == 0)
+ memmove(netname, netname+5, strlen(netname+5)+1);
+ nci->laddr = smprint("%s!%s!%s", netname, nci->lsys, nci->lserv);
+ nci->raddr = smprint("%s!%s!%s", netname, nci->rsys, nci->rserv);
+ if(nci->laddr == nil || nci->raddr == nil)
+ goto err;
+ return nci;
+err:
+ freenetconninfo(nci);
+ return nil;
+}
+
+static void
+xfree(char *x)
+{
+ if(x == nil || x == unknown)
+ return;
+ free(x);
+}
+
+void
+freenetconninfo(NetConnInfo *nci)
+{
+ if(nci == nil)
+ return;
+ xfree(nci->root);
+ xfree(nci->dir);
+ xfree(nci->spec);
+ xfree(nci->lsys);
+ xfree(nci->lserv);
+ xfree(nci->rsys);
+ xfree(nci->rserv);
+ xfree(nci->laddr);
+ xfree(nci->raddr);
+ free(nci);
+}
diff --git a/sys/src/libc/9sys/getpid.c b/sys/src/libc/9sys/getpid.c
new file mode 100755
index 000000000..9a9c86c13
--- /dev/null
+++ b/sys/src/libc/9sys/getpid.c
@@ -0,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+
+int
+getpid(void)
+{
+ char b[20];
+ int f;
+
+ memset(b, 0, sizeof(b));
+ f = open("#c/pid", 0);
+ if(f >= 0) {
+ read(f, b, sizeof(b));
+ close(f);
+ }
+ return atol(b);
+}
diff --git a/sys/src/libc/9sys/getppid.c b/sys/src/libc/9sys/getppid.c
new file mode 100755
index 000000000..b90b57ee5
--- /dev/null
+++ b/sys/src/libc/9sys/getppid.c
@@ -0,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+
+int
+getppid(void)
+{
+ char b[20];
+ int f;
+
+ memset(b, 0, sizeof(b));
+ f = open("/dev/ppid", 0);
+ if(f >= 0) {
+ read(f, b, sizeof(b));
+ close(f);
+ }
+ return atol(b);
+}
diff --git a/sys/src/libc/9sys/getwd.c b/sys/src/libc/9sys/getwd.c
new file mode 100755
index 000000000..ed73cb775
--- /dev/null
+++ b/sys/src/libc/9sys/getwd.c
@@ -0,0 +1,19 @@
+#include <u.h>
+#include <libc.h>
+
+static char *nsgetwd(char*, int);
+
+char*
+getwd(char *buf, int nbuf)
+{
+ int n, fd;
+
+ fd = open(".", OREAD);
+ if(fd < 0)
+ return nil;
+ n = fd2path(fd, buf, nbuf);
+ close(fd);
+ if(n < 0)
+ return nil;
+ return buf;
+}
diff --git a/sys/src/libc/9sys/iounit.c b/sys/src/libc/9sys/iounit.c
new file mode 100755
index 000000000..194b17173
--- /dev/null
+++ b/sys/src/libc/9sys/iounit.c
@@ -0,0 +1,27 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ * Format:
+ 3 r M 4 (0000000000457def 11 00) 8192 512 /rc/lib/rcmain
+ */
+
+int
+iounit(int fd)
+{
+ int i, cfd;
+ char buf[128], *args[10];
+
+ snprint(buf, sizeof buf, "#d/%dctl", fd);
+ cfd = open(buf, OREAD);
+ if(cfd < 0)
+ return 0;
+ i = read(cfd, buf, sizeof buf-1);
+ close(cfd);
+ if(i <= 0)
+ return 0;
+ buf[i] = '\0';
+ if(tokenize(buf, args, nelem(args)) != nelem(args))
+ return 0;
+ return atoi(args[7]);
+}
diff --git a/sys/src/libc/9sys/mkfile b/sys/src/libc/9sys/mkfile
new file mode 100755
index 000000000..3b7d95e8e
--- /dev/null
+++ b/sys/src/libc/9sys/mkfile
@@ -0,0 +1,64 @@
+</$objtype/mkfile
+
+LIB=/$objtype/lib/libc.a
+OFILES=\
+ abort.$O\
+ access.$O\
+ announce.$O\
+ convD2M.$O\
+ convM2D.$O\
+ convM2S.$O\
+ convS2M.$O\
+ cputime.$O\
+ ctime.$O\
+ dial.$O\
+ dirfstat.$O\
+ dirfwstat.$O\
+ dirmodefmt.$O\
+ dirread.$O\
+ dirstat.$O\
+ dirwstat.$O\
+ fcallfmt.$O\
+ fork.$O\
+ getnetconninfo.$O\
+ getenv.$O\
+ getpid.$O\
+ getppid.$O\
+ getwd.$O\
+ iounit.$O\
+ nsec.$O\
+ nulldir.$O\
+ postnote.$O\
+ privalloc.$O\
+ pushssl.$O\
+ pushtls.$O\
+ putenv.$O\
+ qlock.$O\
+ read.$O\
+ read9pmsg.$O\
+ readv.$O\
+ rerrstr.$O\
+ sbrk.$O\
+ setnetmtpt.$O\
+ sysfatal.$O\
+ syslog.$O\
+ sysname.$O\
+ time.$O\
+ times.$O\
+ tm2sec.$O\
+ truerand.$O\
+ wait.$O\
+ waitpid.$O\
+ werrstr.$O\
+ write.$O\
+ writev.$O\
+
+HFILES=/sys/include/libc.h
+
+UPDATE=\
+ mkfile\
+ $HFILES\
+ ${OFILES:%.$O=%.c}\
+
+</sys/src/cmd/mksyslib
+
diff --git a/sys/src/libc/9sys/nsec.c b/sys/src/libc/9sys/nsec.c
new file mode 100755
index 000000000..f0e981d04
--- /dev/null
+++ b/sys/src/libc/9sys/nsec.c
@@ -0,0 +1,75 @@
+#include <u.h>
+#include <libc.h>
+#include <tos.h>
+
+static uvlong order = 0x0001020304050607ULL;
+
+static void
+be2vlong(vlong *to, uchar *f)
+{
+ uchar *t, *o;
+ int i;
+
+ t = (uchar*)to;
+ o = (uchar*)&order;
+ for(i = 0; i < sizeof order; i++)
+ t[o[i]] = f[i];
+}
+
+static int fd = -1;
+static struct {
+ int pid;
+ int fd;
+} fds[64];
+
+vlong
+nsec(void)
+{
+ uchar b[8];
+ vlong t;
+ int pid, i, f, tries;
+
+ /*
+ * Threaded programs may have multiple procs
+ * with different fd tables, so we may need to open
+ * /dev/bintime on a per-pid basis
+ */
+
+ /* First, look if we've opened it for this particular pid */
+ pid = _tos->pid;
+ do{
+ f = -1;
+ for(i = 0; i < nelem(fds); i++)
+ if(fds[i].pid == pid){
+ f = fds[i].fd;
+ break;
+ }
+ tries = 0;
+ if(f < 0){
+ /* If it's not open for this pid, try the global pid */
+ if(fd >= 0)
+ f = fd;
+ else{
+ /* must open */
+ if((f = open("/dev/bintime", OREAD|OCEXEC)) < 0)
+ return 0;
+ fd = f;
+ for(i = 0; i < nelem(fds); i++)
+ if(fds[i].pid == pid || fds[i].pid == 0){
+ fds[i].pid = pid;
+ fds[i].fd = f;
+ break;
+ }
+ }
+ }
+ if(pread(f, b, sizeof b, 0) == sizeof b){
+ be2vlong(&t, b);
+ return t;
+ }
+ close(f);
+ if(i < nelem(fds))
+ fds[i].fd = -1;
+ }while(tries++ == 0); /* retry once */
+ USED(tries);
+ return 0;
+}
diff --git a/sys/src/libc/9sys/nulldir.c b/sys/src/libc/9sys/nulldir.c
new file mode 100755
index 000000000..612725d81
--- /dev/null
+++ b/sys/src/libc/9sys/nulldir.c
@@ -0,0 +1,9 @@
+#include <u.h>
+#include <libc.h>
+
+void
+nulldir(Dir *d)
+{
+ memset(d, ~0, sizeof(Dir));
+ d->name = d->uid = d->gid = d->muid = "";
+}
diff --git a/sys/src/libc/9sys/postnote.c b/sys/src/libc/9sys/postnote.c
new file mode 100755
index 000000000..46564e9ea
--- /dev/null
+++ b/sys/src/libc/9sys/postnote.c
@@ -0,0 +1,32 @@
+#include <u.h>
+#include <libc.h>
+
+int
+postnote(int group, int pid, char *note)
+{
+ char file[128];
+ int f, r;
+
+ switch(group) {
+ case PNPROC:
+ sprint(file, "/proc/%d/note", pid);
+ break;
+ case PNGROUP:
+ sprint(file, "/proc/%d/notepg", pid);
+ break;
+ default:
+ return -1;
+ }
+
+ f = open(file, OWRITE);
+ if(f < 0)
+ return -1;
+
+ r = strlen(note);
+ if(write(f, note, r) != r) {
+ close(f);
+ return -1;
+ }
+ close(f);
+ return 0;
+}
diff --git a/sys/src/libc/9sys/privalloc.c b/sys/src/libc/9sys/privalloc.c
new file mode 100755
index 000000000..907485e2f
--- /dev/null
+++ b/sys/src/libc/9sys/privalloc.c
@@ -0,0 +1,45 @@
+#include <u.h>
+#include <libc.h>
+
+static Lock privlock;
+static int privinit;
+static void **privs;
+
+extern void **_privates;
+extern int _nprivates;
+
+void **
+privalloc(void)
+{
+ void **p;
+ int i;
+
+ lock(&privlock);
+ if(!privinit){
+ privinit = 1;
+ if(_nprivates){
+ _privates[0] = 0;
+ for(i = 1; i < _nprivates; i++)
+ _privates[i] = &_privates[i - 1];
+ privs = &_privates[i - 1];
+ }
+ }
+ p = privs;
+ if(p != nil){
+ privs = *p;
+ *p = nil;
+ }
+ unlock(&privlock);
+ return p;
+}
+
+void
+privfree(void **p)
+{
+ lock(&privlock);
+ if(p != nil && privinit){
+ *p = privs;
+ privs = p;
+ }
+ unlock(&privlock);
+}
diff --git a/sys/src/libc/9sys/pushssl.c b/sys/src/libc/9sys/pushssl.c
new file mode 100755
index 000000000..8817dd1c3
--- /dev/null
+++ b/sys/src/libc/9sys/pushssl.c
@@ -0,0 +1,44 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ * Since the SSL device uses decimal file descriptors to name channels,
+ * it is impossible for a user-level file server to stand in for the kernel device.
+ * Thus we hard-code #D rather than use /net/ssl.
+ */
+
+int
+pushssl(int fd, char *alg, char *secin, char *secout, int *cfd)
+{
+ char buf[8];
+ char dname[64];
+ int n, data, ctl;
+
+ ctl = open("#D/ssl/clone", ORDWR);
+ if(ctl < 0)
+ return -1;
+ n = read(ctl, buf, sizeof(buf)-1);
+ if(n < 0)
+ goto error;
+ buf[n] = 0;
+ sprint(dname, "#D/ssl/%s/data", buf);
+ data = open(dname, ORDWR);
+ if(data < 0)
+ goto error;
+ if(fprint(ctl, "fd %d", fd) < 0 ||
+ fprint(ctl, "secretin %s", secin) < 0 ||
+ fprint(ctl, "secretout %s", secout) < 0 ||
+ fprint(ctl, "alg %s", alg) < 0){
+ close(data);
+ goto error;
+ }
+ close(fd);
+ if(cfd != 0)
+ *cfd = ctl;
+ else
+ close(ctl);
+ return data;
+error:
+ close(ctl);
+ return -1;
+}
diff --git a/sys/src/libc/9sys/pushtls.c b/sys/src/libc/9sys/pushtls.c
new file mode 100755
index 000000000..038aad748
--- /dev/null
+++ b/sys/src/libc/9sys/pushtls.c
@@ -0,0 +1,99 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <mp.h>
+#include <libsec.h>
+
+enum {
+ TLSFinishedLen = 12,
+ HFinished = 20,
+};
+
+static int
+finished(int hand, int isclient)
+{
+ int i, n;
+ uchar buf[500], buf2[500];
+
+ buf[0] = HFinished;
+ buf[1] = TLSFinishedLen>>16;
+ buf[2] = TLSFinishedLen>>8;
+ buf[3] = TLSFinishedLen;
+ n = TLSFinishedLen+4;
+
+ for(i=0; i<2; i++){
+ if(i==0)
+ memmove(buf+4, "client finished", TLSFinishedLen);
+ else
+ memmove(buf+4, "server finished", TLSFinishedLen);
+ if(isclient == 1-i){
+ if(write(hand, buf, n) != n)
+ return -1;
+ }else{
+ if(readn(hand, buf2, n) != n || memcmp(buf,buf2,n) != 0)
+ return -1;
+ }
+ }
+ return 1;
+}
+
+
+// given a plain fd and secrets established beforehand, return encrypted connection
+int
+pushtls(int fd, char *hashalg, char *encalg, int isclient, char *secret, char *dir)
+{
+ char buf[8];
+ char dname[64];
+ int n, data, ctl, hand;
+
+ // open a new filter; get ctl fd
+ data = hand = -1;
+ // /net/tls uses decimal file descriptors to name channels, hence a
+ // user-level file server can't stand in for #a; may as well hard-code it.
+ ctl = open("#a/tls/clone", ORDWR);
+ if(ctl < 0)
+ goto error;
+ n = read(ctl, buf, sizeof(buf)-1);
+ if(n < 0)
+ goto error;
+ buf[n] = 0;
+ if(dir)
+ sprint(dir, "#a/tls/%s", buf);
+
+ // get application fd
+ sprint(dname, "#a/tls/%s/data", buf);
+ data = open(dname, ORDWR);
+ if(data < 0)
+ goto error;
+
+ // get handshake fd
+ sprint(dname, "#a/tls/%s/hand", buf);
+ hand = open(dname, ORDWR);
+ if(hand < 0)
+ goto error;
+
+ // speak a minimal handshake
+ if(fprint(ctl, "fd %d 0x301", fd) < 0 ||
+ fprint(ctl, "version 0x301") < 0 ||
+ fprint(ctl, "secret %s %s %d %s", hashalg, encalg, isclient, secret) < 0 ||
+ fprint(ctl, "changecipher") < 0 ||
+ finished(hand, isclient) < 0 ||
+ fprint(ctl, "opened") < 0){
+ close(hand);
+ hand = -1;
+ goto error;
+ }
+ close(ctl);
+ close(hand);
+ close(fd);
+ return data;
+
+error:
+ if(data>=0)
+ close(data);
+ if(ctl>=0)
+ close(ctl);
+ if(hand>=0)
+ close(hand);
+ return -1;
+}
diff --git a/sys/src/libc/9sys/putenv.c b/sys/src/libc/9sys/putenv.c
new file mode 100755
index 000000000..de2389482
--- /dev/null
+++ b/sys/src/libc/9sys/putenv.c
@@ -0,0 +1,26 @@
+#include <u.h>
+#include <libc.h>
+
+int
+putenv(char *name, char *val)
+{
+ int f;
+ char ename[100];
+ long s;
+
+ if(strchr(name, '/') != nil)
+ return -1;
+ snprint(ename, sizeof ename, "/env/%s", name);
+ if(strcmp(ename+5, name) != 0)
+ return -1;
+ f = create(ename, OWRITE, 0664);
+ if(f < 0)
+ return -1;
+ s = strlen(val);
+ if(write(f, val, s) != s){
+ close(f);
+ return -1;
+ }
+ close(f);
+ return 0;
+}
diff --git a/sys/src/libc/9sys/qlock.c b/sys/src/libc/9sys/qlock.c
new file mode 100755
index 000000000..fb2d80238
--- /dev/null
+++ b/sys/src/libc/9sys/qlock.c
@@ -0,0 +1,363 @@
+#include <u.h>
+#include <libc.h>
+
+static struct {
+ QLp *p;
+ QLp x[1024];
+} ql = {
+ ql.x
+};
+
+enum
+{
+ Queuing,
+ QueuingR,
+ QueuingW,
+ Sleeping,
+};
+
+static void* (*_rendezvousp)(void*, void*) = rendezvous;
+
+/* this gets called by the thread library ONLY to get us to use its rendezvous */
+void
+_qlockinit(void* (*r)(void*, void*))
+{
+ _rendezvousp = r;
+}
+
+/* find a free shared memory location to queue ourselves in */
+static QLp*
+getqlp(void)
+{
+ QLp *p, *op;
+
+ op = ql.p;
+ for(p = op+1; ; p++){
+ if(p == &ql.x[nelem(ql.x)])
+ p = ql.x;
+ if(p == op)
+ abort();
+ if(_tas(&(p->inuse)) == 0){
+ ql.p = p;
+ p->next = nil;
+ break;
+ }
+ }
+ return p;
+}
+
+void
+qlock(QLock *q)
+{
+ QLp *p, *mp;
+
+ lock(&q->lock);
+ if(!q->locked){
+ q->locked = 1;
+ unlock(&q->lock);
+ return;
+ }
+
+
+ /* chain into waiting list */
+ mp = getqlp();
+ p = q->tail;
+ if(p == nil)
+ q->head = mp;
+ else
+ p->next = mp;
+ q->tail = mp;
+ mp->state = Queuing;
+ unlock(&q->lock);
+
+ /* wait */
+ while((*_rendezvousp)(mp, (void*)1) == (void*)~0)
+ ;
+ mp->inuse = 0;
+}
+
+void
+qunlock(QLock *q)
+{
+ QLp *p;
+
+ lock(&q->lock);
+ if (q->locked == 0)
+ fprint(2, "qunlock called with qlock not held, from %#p\n",
+ getcallerpc(&q));
+ p = q->head;
+ if(p != nil){
+ /* wakeup head waiting process */
+ q->head = p->next;
+ if(q->head == nil)
+ q->tail = nil;
+ unlock(&q->lock);
+ while((*_rendezvousp)(p, (void*)0x12345) == (void*)~0)
+ ;
+ return;
+ }
+ q->locked = 0;
+ unlock(&q->lock);
+}
+
+int
+canqlock(QLock *q)
+{
+ if(!canlock(&q->lock))
+ return 0;
+ if(!q->locked){
+ q->locked = 1;
+ unlock(&q->lock);
+ return 1;
+ }
+ unlock(&q->lock);
+ return 0;
+}
+
+void
+rlock(RWLock *q)
+{
+ QLp *p, *mp;
+
+ lock(&q->lock);
+ if(q->writer == 0 && q->head == nil){
+ /* no writer, go for it */
+ q->readers++;
+ unlock(&q->lock);
+ return;
+ }
+
+ mp = getqlp();
+ p = q->tail;
+ if(p == 0)
+ q->head = mp;
+ else
+ p->next = mp;
+ q->tail = mp;
+ mp->next = nil;
+ mp->state = QueuingR;
+ unlock(&q->lock);
+
+ /* wait in kernel */
+ while((*_rendezvousp)(mp, (void*)1) == (void*)~0)
+ ;
+ mp->inuse = 0;
+}
+
+int
+canrlock(RWLock *q)
+{
+ lock(&q->lock);
+ if (q->writer == 0 && q->head == nil) {
+ /* no writer; go for it */
+ q->readers++;
+ unlock(&q->lock);
+ return 1;
+ }
+ unlock(&q->lock);
+ return 0;
+}
+
+void
+runlock(RWLock *q)
+{
+ QLp *p;
+
+ lock(&q->lock);
+ if(q->readers <= 0)
+ abort();
+ p = q->head;
+ if(--(q->readers) > 0 || p == nil){
+ unlock(&q->lock);
+ return;
+ }
+
+ /* start waiting writer */
+ if(p->state != QueuingW)
+ abort();
+ q->head = p->next;
+ if(q->head == 0)
+ q->tail = 0;
+ q->writer = 1;
+ unlock(&q->lock);
+
+ /* wakeup waiter */
+ while((*_rendezvousp)(p, 0) == (void*)~0)
+ ;
+}
+
+void
+wlock(RWLock *q)
+{
+ QLp *p, *mp;
+
+ lock(&q->lock);
+ if(q->readers == 0 && q->writer == 0){
+ /* noone waiting, go for it */
+ q->writer = 1;
+ unlock(&q->lock);
+ return;
+ }
+
+ /* wait */
+ p = q->tail;
+ mp = getqlp();
+ if(p == nil)
+ q->head = mp;
+ else
+ p->next = mp;
+ q->tail = mp;
+ mp->next = nil;
+ mp->state = QueuingW;
+ unlock(&q->lock);
+
+ /* wait in kernel */
+ while((*_rendezvousp)(mp, (void*)1) == (void*)~0)
+ ;
+ mp->inuse = 0;
+}
+
+int
+canwlock(RWLock *q)
+{
+ lock(&q->lock);
+ if (q->readers == 0 && q->writer == 0) {
+ /* no one waiting; go for it */
+ q->writer = 1;
+ unlock(&q->lock);
+ return 1;
+ }
+ unlock(&q->lock);
+ return 0;
+}
+
+void
+wunlock(RWLock *q)
+{
+ QLp *p;
+
+ lock(&q->lock);
+ if(q->writer == 0)
+ abort();
+ p = q->head;
+ if(p == nil){
+ q->writer = 0;
+ unlock(&q->lock);
+ return;
+ }
+ if(p->state == QueuingW){
+ /* start waiting writer */
+ q->head = p->next;
+ if(q->head == nil)
+ q->tail = nil;
+ unlock(&q->lock);
+ while((*_rendezvousp)(p, 0) == (void*)~0)
+ ;
+ return;
+ }
+
+ if(p->state != QueuingR)
+ abort();
+
+ /* wake waiting readers */
+ while(q->head != nil && q->head->state == QueuingR){
+ p = q->head;
+ q->head = p->next;
+ q->readers++;
+ while((*_rendezvousp)(p, 0) == (void*)~0)
+ ;
+ }
+ if(q->head == nil)
+ q->tail = nil;
+ q->writer = 0;
+ unlock(&q->lock);
+}
+
+void
+rsleep(Rendez *r)
+{
+ QLp *t, *me;
+
+ if(!r->l)
+ abort();
+ lock(&r->l->lock);
+ /* we should hold the qlock */
+ if(!r->l->locked)
+ abort();
+
+ /* add ourselves to the wait list */
+ me = getqlp();
+ me->state = Sleeping;
+ if(r->head == nil)
+ r->head = me;
+ else
+ r->tail->next = me;
+ me->next = nil;
+ r->tail = me;
+
+ /* pass the qlock to the next guy */
+ t = r->l->head;
+ if(t){
+ r->l->head = t->next;
+ if(r->l->head == nil)
+ r->l->tail = nil;
+ unlock(&r->l->lock);
+ while((*_rendezvousp)(t, (void*)0x12345) == (void*)~0)
+ ;
+ }else{
+ r->l->locked = 0;
+ unlock(&r->l->lock);
+ }
+
+ /* wait for a wakeup */
+ while((*_rendezvousp)(me, (void*)1) == (void*)~0)
+ ;
+ me->inuse = 0;
+}
+
+int
+rwakeup(Rendez *r)
+{
+ QLp *t;
+
+ /*
+ * take off wait and put on front of queue
+ * put on front so guys that have been waiting will not get starved
+ */
+
+ if(!r->l)
+ abort();
+ lock(&r->l->lock);
+ if(!r->l->locked)
+ abort();
+
+ t = r->head;
+ if(t == nil){
+ unlock(&r->l->lock);
+ return 0;
+ }
+
+ r->head = t->next;
+ if(r->head == nil)
+ r->tail = nil;
+
+ t->next = r->l->head;
+ r->l->head = t;
+ if(r->l->tail == nil)
+ r->l->tail = t;
+
+ t->state = Queuing;
+ unlock(&r->l->lock);
+ return 1;
+}
+
+int
+rwakeupall(Rendez *r)
+{
+ int i;
+
+ for(i=0; rwakeup(r); i++)
+ ;
+ return i;
+}
+
diff --git a/sys/src/libc/9sys/read.c b/sys/src/libc/9sys/read.c
new file mode 100755
index 000000000..2119ab2ac
--- /dev/null
+++ b/sys/src/libc/9sys/read.c
@@ -0,0 +1,8 @@
+#include <u.h>
+#include <libc.h>
+
+long
+read(int fd, void *buf, long n)
+{
+ return pread(fd, buf, n, -1LL);
+}
diff --git a/sys/src/libc/9sys/read9pmsg.c b/sys/src/libc/9sys/read9pmsg.c
new file mode 100755
index 000000000..9e90ec5d6
--- /dev/null
+++ b/sys/src/libc/9sys/read9pmsg.c
@@ -0,0 +1,31 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+int
+read9pmsg(int fd, void *abuf, uint n)
+{
+ int m, len;
+ uchar *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){
+ werrstr("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/sys/src/libc/9sys/readv.c b/sys/src/libc/9sys/readv.c
new file mode 100755
index 000000000..a0dc10aaf
--- /dev/null
+++ b/sys/src/libc/9sys/readv.c
@@ -0,0 +1,49 @@
+#include <u.h>
+#include <libc.h>
+
+static
+long
+ioreadv(int fd, IOchunk *io, int nio, vlong offset)
+{
+ int i;
+ long m, n, tot;
+ char *buf, *p;
+
+ tot = 0;
+ for(i=0; i<nio; i++)
+ tot += io[i].len;
+ buf = malloc(tot);
+ if(buf == nil)
+ return -1;
+
+ tot = pread(fd, buf, tot, offset);
+
+ p = buf;
+ n = tot;
+ for(i=0; i<nio; i++){
+ if(n <= 0)
+ break;
+ m = io->len;
+ if(m > n)
+ m = n;
+ memmove(io->addr, p, m);
+ n -= m;
+ p += m;
+ io++;
+ }
+
+ free(buf);
+ return tot;
+}
+
+long
+readv(int fd, IOchunk *io, int nio)
+{
+ return ioreadv(fd, io, nio, -1LL);
+}
+
+long
+preadv(int fd, IOchunk *io, int nio, vlong off)
+{
+ return ioreadv(fd, io, nio, off);
+}
diff --git a/sys/src/libc/9sys/rerrstr.c b/sys/src/libc/9sys/rerrstr.c
new file mode 100755
index 000000000..c64cd4940
--- /dev/null
+++ b/sys/src/libc/9sys/rerrstr.c
@@ -0,0 +1,13 @@
+#include <u.h>
+#include <libc.h>
+
+void
+rerrstr(char *buf, uint nbuf)
+{
+ char tmp[ERRMAX];
+
+ tmp[0] = 0;
+ errstr(tmp, sizeof tmp);
+ utfecpy(buf, buf+nbuf, tmp);
+ errstr(tmp, sizeof tmp);
+}
diff --git a/sys/src/libc/9sys/sbrk.c b/sys/src/libc/9sys/sbrk.c
new file mode 100755
index 000000000..88b593e11
--- /dev/null
+++ b/sys/src/libc/9sys/sbrk.c
@@ -0,0 +1,35 @@
+#include <u.h>
+#include <libc.h>
+
+extern char end[];
+static char *bloc = { end };
+extern int brk_(void*);
+
+enum
+{
+ Round = 7
+};
+
+int
+brk(void *p)
+{
+ uintptr bl;
+
+ bl = ((uintptr)p + Round) & ~Round;
+ if(brk_((void*)bl) < 0)
+ return -1;
+ bloc = (char*)bl;
+ return 0;
+}
+
+void*
+sbrk(ulong n)
+{
+ uintptr bl;
+
+ bl = ((uintptr)bloc + Round) & ~Round;
+ if(brk_((void*)(bl+n)) < 0)
+ return (void*)-1;
+ bloc = (char*)bl + n;
+ return (void*)bl;
+}
diff --git a/sys/src/libc/9sys/setnetmtpt.c b/sys/src/libc/9sys/setnetmtpt.c
new file mode 100755
index 000000000..3d984f08f
--- /dev/null
+++ b/sys/src/libc/9sys/setnetmtpt.c
@@ -0,0 +1,16 @@
+#include <u.h>
+#include <libc.h>
+
+void
+setnetmtpt(char *net, int n, char *x)
+{
+ if(x == nil)
+ x = "/net";
+
+ if(*x == '/'){
+ strncpy(net, x, n);
+ net[n-1] = 0;
+ } else {
+ snprint(net, n, "/net%s", x);
+ }
+}
diff --git a/sys/src/libc/9sys/sysfatal.c b/sys/src/libc/9sys/sysfatal.c
new file mode 100755
index 000000000..e2cef6c48
--- /dev/null
+++ b/sys/src/libc/9sys/sysfatal.c
@@ -0,0 +1,28 @@
+#include <u.h>
+#include <libc.h>
+
+
+static void
+_sysfatalimpl(char *fmt, va_list arg)
+{
+ char buf[1024];
+
+ vseprint(buf, buf+sizeof(buf), fmt, arg);
+ if(argv0)
+ fprint(2, "%s: %s\n", argv0, buf);
+ else
+ fprint(2, "%s\n", buf);
+ exits(buf);
+}
+
+void (*_sysfatal)(char *fmt, va_list arg) = _sysfatalimpl;
+
+void
+sysfatal(char *fmt, ...)
+{
+ va_list arg;
+
+ va_start(arg, fmt);
+ (*_sysfatal)(fmt, arg);
+ va_end(arg);
+}
diff --git a/sys/src/libc/9sys/syslog.c b/sys/src/libc/9sys/syslog.c
new file mode 100755
index 000000000..828ad04a7
--- /dev/null
+++ b/sys/src/libc/9sys/syslog.c
@@ -0,0 +1,117 @@
+#include <u.h>
+#include <libc.h>
+
+static struct
+{
+ int fd;
+ int consfd;
+ char *name;
+ Dir *d;
+ Dir *consd;
+ Lock;
+} sl =
+{
+ -1, -1,
+};
+
+static void
+_syslogopen(void)
+{
+ char buf[1024];
+
+ if(sl.fd >= 0)
+ close(sl.fd);
+ snprint(buf, sizeof(buf), "/sys/log/%s", sl.name);
+ sl.fd = open(buf, OWRITE|OCEXEC);
+}
+
+/*
+ * Print
+ * sysname: time: mesg
+ * on /sys/log/logname.
+ * If cons or log file can't be opened, print on the system console, too.
+ */
+void
+syslog(int cons, char *logname, char *fmt, ...)
+{
+ char buf[1024];
+ char *ctim, *p;
+ va_list arg;
+ int n;
+ Dir *d;
+ char err[ERRMAX];
+
+ err[0] = '\0';
+ errstr(err, sizeof err);
+ lock(&sl);
+
+ /*
+ * paranoia makes us stat to make sure a fork+close
+ * hasn't broken our fd's
+ */
+ d = dirfstat(sl.fd);
+ if(sl.fd < 0
+ || sl.name == nil
+ || strcmp(sl.name, logname)!=0
+ || sl.d == nil
+ || d == nil
+ || d->dev != sl.d->dev
+ || d->type != sl.d->type
+ || d->qid.path != sl.d->qid.path){
+ free(sl.name);
+ sl.name = strdup(logname);
+ if(sl.name == nil)
+ cons = 1;
+ else{
+ _syslogopen();
+ if(sl.fd < 0)
+ cons = 1;
+ free(sl.d);
+ sl.d = d;
+ d = nil; /* don't free it */
+ }
+ }
+ free(d);
+ if(cons){
+ d = dirfstat(sl.consfd);
+ if(sl.consfd < 0
+ || d == nil
+ || sl.consd == nil
+ || d->dev != sl.consd->dev
+ || d->type != sl.consd->type
+ || d->qid.path != sl.consd->qid.path){
+ sl.consfd = open("#c/cons", OWRITE|OCEXEC);
+ free(sl.consd);
+ sl.consd = d;
+ d = nil; /* don't free it */
+ }
+ free(d);
+ }
+
+ if(fmt == nil){
+ unlock(&sl);
+ return;
+ }
+
+ ctim = ctime(time(0));
+ werrstr(err);
+ p = buf + snprint(buf, sizeof(buf)-1, "%s ", sysname());
+ strncpy(p, ctim+4, 15);
+ p += 15;
+ *p++ = ' ';
+ va_start(arg, fmt);
+ p = vseprint(p, buf+sizeof(buf)-1, fmt, arg);
+ va_end(arg);
+ *p++ = '\n';
+ n = p - buf;
+
+ if(sl.fd >= 0){
+ seek(sl.fd, 0, 2);
+ write(sl.fd, buf, n);
+ }
+
+ if(cons && sl.consfd >=0)
+ write(sl.consfd, buf, n);
+
+ unlock(&sl);
+}
diff --git a/sys/src/libc/9sys/sysname.c b/sys/src/libc/9sys/sysname.c
new file mode 100755
index 000000000..1854ddceb
--- /dev/null
+++ b/sys/src/libc/9sys/sysname.c
@@ -0,0 +1,21 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+sysname(void)
+{
+ int f, n;
+ static char b[128];
+
+ if(b[0])
+ return b;
+
+ f = open("#c/sysname", 0);
+ if(f >= 0) {
+ n = read(f, b, sizeof(b)-1);
+ if(n > 0)
+ b[n] = 0;
+ close(f);
+ }
+ return b;
+}
diff --git a/sys/src/libc/9sys/time.c b/sys/src/libc/9sys/time.c
new file mode 100755
index 000000000..3e5f83b04
--- /dev/null
+++ b/sys/src/libc/9sys/time.c
@@ -0,0 +1,51 @@
+#include <u.h>
+#include <libc.h>
+
+
+/*
+ * After a fork with fd's copied, both fd's are pointing to
+ * the same Chan structure. Since the offset is kept in the Chan
+ * structure, the seek's and read's in the two processes can
+ * compete at moving the offset around. Hence the unusual loop
+ * in the middle of this routine.
+ */
+static long
+oldtime(long *tp)
+{
+ char b[20];
+ static int f = -1;
+ int i, retries;
+ long t;
+
+ memset(b, 0, sizeof(b));
+ for(retries = 0; retries < 100; retries++){
+ if(f < 0)
+ f = open("/dev/time", OREAD|OCEXEC);
+ if(f < 0)
+ break;
+ if(seek(f, 0, 0) < 0 || (i = read(f, b, sizeof(b))) < 0){
+ close(f);
+ f = -1;
+ } else {
+ if(i != 0)
+ break;
+ }
+ }
+ t = atol(b);
+ if(tp)
+ *tp = t;
+ return t;
+}
+
+long
+time(long *tp)
+{
+ vlong t;
+
+ t = nsec()/1000000000LL;
+ if(t == 0)
+ t = oldtime(0);
+ if(tp != nil)
+ *tp = t;
+ return t;
+}
diff --git a/sys/src/libc/9sys/times.c b/sys/src/libc/9sys/times.c
new file mode 100755
index 000000000..b8303474f
--- /dev/null
+++ b/sys/src/libc/9sys/times.c
@@ -0,0 +1,60 @@
+#include <u.h>
+#include <libc.h>
+
+static
+char*
+skip(char *p)
+{
+
+ while(*p == ' ')
+ p++;
+ while(*p != ' ' && *p != 0)
+ p++;
+ return p;
+}
+
+/*
+ * after a fork with fd's copied, both fd's are pointing to
+ * the same Chan structure. Since the offset is kept in the Chan
+ * structure, the seek's and read's in the two processes can be
+ * are competing moving the offset around. Hence the unusual loop
+ * in the middle of this routine.
+ */
+long
+times(long *t)
+{
+ char b[200], *p;
+ static int f = -1;
+ int i, retries;
+ ulong r;
+
+ memset(b, 0, sizeof(b));
+ for(retries = 0; retries < 100; retries++){
+ if(f < 0)
+ f = open("/dev/cputime", OREAD|OCEXEC);
+ if(f < 0)
+ break;
+ if(seek(f, 0, 0) < 0 || (i = read(f, b, sizeof(b))) < 0){
+ close(f);
+ f = -1;
+ } else {
+ if(i != 0)
+ break;
+ }
+ }
+ p = b;
+ if(t)
+ t[0] = atol(p);
+ p = skip(p);
+ if(t)
+ t[1] = atol(p);
+ p = skip(p);
+ r = atol(p);
+ if(t){
+ p = skip(p);
+ t[2] = atol(p);
+ p = skip(p);
+ t[3] = atol(p);
+ }
+ return r;
+}
diff --git a/sys/src/libc/9sys/tm2sec.c b/sys/src/libc/9sys/tm2sec.c
new file mode 100755
index 000000000..223d3f1d9
--- /dev/null
+++ b/sys/src/libc/9sys/tm2sec.c
@@ -0,0 +1,194 @@
+#include <u.h>
+#include <libc.h>
+
+#define TZSIZE 150
+static void readtimezone(void);
+static int rd_name(char**, char*);
+static int rd_long(char**, long*);
+static
+struct
+{
+ char stname[4];
+ char dlname[4];
+ long stdiff;
+ long dldiff;
+ long dlpairs[TZSIZE];
+} timezone;
+
+#define SEC2MIN 60L
+#define SEC2HOUR (60L*SEC2MIN)
+#define SEC2DAY (24L*SEC2HOUR)
+
+/*
+ * days per month plus days/year
+ */
+static int dmsize[] =
+{
+ 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+static int ldmsize[] =
+{
+ 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+/*
+ * return the days/month for the given year
+ */
+static int *
+yrsize(int y)
+{
+ if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0))
+ return ldmsize;
+ else
+ return dmsize;
+}
+
+/*
+ * compute seconds since Jan 1 1970 GMT
+ * and convert to our timezone.
+ */
+long
+tm2sec(Tm *tm)
+{
+ long secs;
+ int i, yday, year, *d2m;
+
+ if(strcmp(tm->zone, "GMT") != 0 && timezone.stname[0] == 0)
+ readtimezone();
+ secs = 0;
+
+ /*
+ * seconds per year
+ */
+ year = tm->year + 1900;
+ for(i = 1970; i < year; i++){
+ d2m = yrsize(i);
+ secs += d2m[0] * SEC2DAY;
+ }
+
+ /*
+ * if mday is set, use mon and mday to compute yday
+ */
+ if(tm->mday){
+ yday = 0;
+ d2m = yrsize(year);
+ for(i=0; i<tm->mon; i++)
+ yday += d2m[i+1];
+ yday += tm->mday-1;
+ }else{
+ yday = tm->yday;
+ }
+ secs += yday * SEC2DAY;
+
+ /*
+ * hours, minutes, seconds
+ */
+ secs += tm->hour * SEC2HOUR;
+ secs += tm->min * SEC2MIN;
+ secs += tm->sec;
+
+ /*
+ * Only handles zones mentioned in /env/timezone,
+ * but things get too ambiguous otherwise.
+ */
+ if(strcmp(tm->zone, timezone.stname) == 0)
+ secs -= timezone.stdiff;
+ else if(strcmp(tm->zone, timezone.dlname) == 0)
+ secs -= timezone.dldiff;
+ if(secs < 0)
+ secs = 0;
+ return secs;
+}
+
+static
+void
+readtimezone(void)
+{
+ char buf[TZSIZE*11+30], *p;
+ int i;
+
+ memset(buf, 0, sizeof(buf));
+ i = open("/env/timezone", 0);
+ if(i < 0)
+ goto error;
+ if(read(i, buf, sizeof(buf)) >= sizeof(buf))
+ goto error;
+ close(i);
+ p = buf;
+ if(rd_name(&p, timezone.stname))
+ goto error;
+ if(rd_long(&p, &timezone.stdiff))
+ goto error;
+ if(rd_name(&p, timezone.dlname))
+ goto error;
+ if(rd_long(&p, &timezone.dldiff))
+ goto error;
+ for(i=0; i<TZSIZE; i++) {
+ if(rd_long(&p, &timezone.dlpairs[i]))
+ goto error;
+ if(timezone.dlpairs[i] == 0)
+ return;
+ }
+
+error:
+ timezone.stdiff = 0;
+ strcpy(timezone.stname, "GMT");
+ timezone.dlpairs[0] = 0;
+}
+
+static int
+rd_name(char **f, char *p)
+{
+ int c, i;
+
+ for(;;) {
+ c = *(*f)++;
+ if(c != ' ' && c != '\n')
+ break;
+ }
+ for(i=0; i<3; i++) {
+ if(c == ' ' || c == '\n')
+ return 1;
+ *p++ = c;
+ c = *(*f)++;
+ }
+ if(c != ' ' && c != '\n')
+ return 1;
+ *p = 0;
+ return 0;
+}
+
+static int
+rd_long(char **f, long *p)
+{
+ int c, s;
+ long l;
+
+ s = 0;
+ for(;;) {
+ c = *(*f)++;
+ if(c == '-') {
+ s++;
+ continue;
+ }
+ if(c != ' ' && c != '\n')
+ break;
+ }
+ if(c == 0) {
+ *p = 0;
+ return 0;
+ }
+ l = 0;
+ for(;;) {
+ if(c == ' ' || c == '\n')
+ break;
+ if(c < '0' || c > '9')
+ return 1;
+ l = l*10 + c-'0';
+ c = *(*f)++;
+ }
+ if(s)
+ l = -l;
+ *p = l;
+ return 0;
+}
diff --git a/sys/src/libc/9sys/truerand.c b/sys/src/libc/9sys/truerand.c
new file mode 100755
index 000000000..10a0f14de
--- /dev/null
+++ b/sys/src/libc/9sys/truerand.c
@@ -0,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+
+ulong
+truerand(void)
+{
+ ulong x;
+ static int randfd = -1;
+
+ if(randfd < 0)
+ randfd = open("/dev/random", OREAD|OCEXEC);
+ if(randfd < 0)
+ sysfatal("can't open /dev/random");
+ if(read(randfd, &x, sizeof(x)) != sizeof(x))
+ sysfatal("can't read /dev/random");
+ return x;
+}
diff --git a/sys/src/libc/9sys/wait.c b/sys/src/libc/9sys/wait.c
new file mode 100755
index 000000000..e2ac817ea
--- /dev/null
+++ b/sys/src/libc/9sys/wait.c
@@ -0,0 +1,32 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+Waitmsg*
+wait(void)
+{
+ int n, l;
+ char buf[512], *fld[5];
+ Waitmsg *w;
+
+ n = await(buf, sizeof buf-1);
+ if(n < 0)
+ return nil;
+ buf[n] = '\0';
+ if(tokenize(buf, fld, nelem(fld)) != nelem(fld)){
+ werrstr("couldn't parse wait message");
+ return nil;
+ }
+ l = strlen(fld[4])+1;
+ w = malloc(sizeof(Waitmsg)+l);
+ if(w == nil)
+ return nil;
+ w->pid = atoi(fld[0]);
+ w->time[0] = atoi(fld[1]);
+ w->time[1] = atoi(fld[2]);
+ w->time[2] = atoi(fld[3]);
+ w->msg = (char*)&w[1];
+ memmove(w->msg, fld[4], l);
+ return w;
+}
+
diff --git a/sys/src/libc/9sys/waitpid.c b/sys/src/libc/9sys/waitpid.c
new file mode 100755
index 000000000..098c428cf
--- /dev/null
+++ b/sys/src/libc/9sys/waitpid.c
@@ -0,0 +1,21 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+int
+waitpid(void)
+{
+ int n;
+ char buf[512], *fld[5];
+
+ n = await(buf, sizeof buf-1);
+ if(n <= 0)
+ return -1;
+ buf[n] = '\0';
+ if(tokenize(buf, fld, nelem(fld)) != nelem(fld)){
+ werrstr("couldn't parse wait message");
+ return -1;
+ }
+ return atoi(fld[0]);
+}
+
diff --git a/sys/src/libc/9sys/werrstr.c b/sys/src/libc/9sys/werrstr.c
new file mode 100755
index 000000000..da542aa17
--- /dev/null
+++ b/sys/src/libc/9sys/werrstr.c
@@ -0,0 +1,14 @@
+#include <u.h>
+#include <libc.h>
+
+void
+werrstr(char *fmt, ...)
+{
+ va_list arg;
+ char buf[ERRMAX];
+
+ va_start(arg, fmt);
+ vseprint(buf, buf+ERRMAX, fmt, arg);
+ va_end(arg);
+ errstr(buf, ERRMAX);
+}
diff --git a/sys/src/libc/9sys/write.c b/sys/src/libc/9sys/write.c
new file mode 100755
index 000000000..5bbd3064e
--- /dev/null
+++ b/sys/src/libc/9sys/write.c
@@ -0,0 +1,8 @@
+#include <u.h>
+#include <libc.h>
+
+long
+write(int fd, void *buf, long n)
+{
+ return pwrite(fd, buf, n, -1LL);
+}
diff --git a/sys/src/libc/9sys/writev.c b/sys/src/libc/9sys/writev.c
new file mode 100755
index 000000000..e3692db27
--- /dev/null
+++ b/sys/src/libc/9sys/writev.c
@@ -0,0 +1,42 @@
+#include <u.h>
+#include <libc.h>
+
+static
+long
+iowritev(int fd, IOchunk *io, int nio, vlong offset)
+{
+ int i;
+ long tot;
+ char *buf, *p;
+
+ tot = 0;
+ for(i=0; i<nio; i++)
+ tot += io[i].len;
+ buf = malloc(tot);
+ if(buf == nil)
+ return -1;
+
+ p = buf;
+ for(i=0; i<nio; i++){
+ memmove(p, io->addr, io->len);
+ p += io->len;
+ io++;
+ }
+
+ tot = pwrite(fd, buf, tot, offset);
+
+ free(buf);
+ return tot;
+}
+
+long
+writev(int fd, IOchunk *io, int nio)
+{
+ return iowritev(fd, io, nio, -1LL);
+}
+
+long
+pwritev(int fd, IOchunk *io, int nio, vlong off)
+{
+ return iowritev(fd, io, nio, off);
+}