diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/unix/drawterm/kern |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/unix/drawterm/kern')
51 files changed, 22519 insertions, 0 deletions
diff --git a/sys/src/cmd/unix/drawterm/kern/Makefile b/sys/src/cmd/unix/drawterm/kern/Makefile new file mode 100755 index 000000000..c4cf4b21b --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/Makefile @@ -0,0 +1,50 @@ +ROOT=.. +include ../Make.config +LIB=libkern.a + +OFILES=\ + allocb.$O\ + cache.$O\ + chan.$O\ + data.$O\ + dev.$O\ + devaudio.$O\ + devaudio-$(AUDIO).$O\ + devcons.$O\ + devdraw.$O\ + devfs-$(OS).$O\ + devip.$O\ + devip-$(OS).$O\ + devlfd.$O\ + devmnt.$O\ + devmouse.$O\ + devpipe.$O\ + devroot.$O\ + devssl.$O\ + devtls.$O\ + devtab.$O\ + error.$O\ + parse.$O\ + pgrp.$O\ + procinit.$O\ + rwlock.$O\ + sleep.$O\ + smalloc.$O\ + stub.$O\ + sysfile.$O\ + sysproc.$O\ + qio.$O\ + qlock.$O\ + term.$O\ + uart.$O\ + waserror.$O\ + $(OS).$O + +default: $(LIB) +$(LIB): $(OFILES) + $(AR) r $(LIB) $(OFILES) + $(RANLIB) $(LIB) + +%.$O: %.c + $(CC) $(CFLAGS) $*.c + diff --git a/sys/src/cmd/unix/drawterm/kern/allocb.c b/sys/src/cmd/unix/drawterm/kern/allocb.c new file mode 100755 index 000000000..ab1595c89 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/allocb.c @@ -0,0 +1,165 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +enum +{ + Hdrspc = 64, /* leave room for high-level headers */ + Bdead = 0x51494F42, /* "QIOB" */ +}; + +struct +{ + Lock lk; + ulong bytes; +} ialloc; + +static Block* +_allocb(int size) +{ + Block *b; + uintptr addr; + + if((b = mallocz(sizeof(Block)+size+Hdrspc, 0)) == nil) + return nil; + + b->next = nil; + b->list = nil; + b->free = 0; + b->flag = 0; + + /* align start of data portion by rounding up */ + addr = (uintptr)b; + addr = ROUND(addr + sizeof(Block), BLOCKALIGN); + b->base = (uchar*)addr; + + /* align end of data portion by rounding down */ + b->lim = ((uchar*)b) + sizeof(Block)+size+Hdrspc; + addr = (uintptr)(b->lim); + addr = addr & ~(BLOCKALIGN-1); + b->lim = (uchar*)addr; + + /* leave sluff at beginning for added headers */ + b->rp = b->lim - ROUND(size, BLOCKALIGN); + if(b->rp < b->base) + panic("_allocb"); + b->wp = b->rp; + + return b; +} + +Block* +allocb(int size) +{ + Block *b; + + /* + * Check in a process and wait until successful. + * Can still error out of here, though. + */ + if(up == nil) + panic("allocb without up: %p\n", getcallerpc(&size)); + if((b = _allocb(size)) == nil){ + panic("allocb: no memory for %d bytes\n", size); + } + setmalloctag(b, getcallerpc(&size)); + + return b; +} + +Block* +iallocb(int size) +{ + Block *b; + static int m1, m2; + + if(ialloc.bytes > conf.ialloc){ + if((m1++%10000)==0) + print("iallocb: limited %lud/%lud\n", + ialloc.bytes, conf.ialloc); + return 0; + } + + if((b = _allocb(size)) == nil){ + if((m2++%10000)==0) + print("iallocb: no memory %lud/%lud\n", + ialloc.bytes, conf.ialloc); + return nil; + } + setmalloctag(b, getcallerpc(&size)); + b->flag = BINTR; + + ilock(&ialloc.lk); + ialloc.bytes += b->lim - b->base; + iunlock(&ialloc.lk); + + return b; +} + +void +freeb(Block *b) +{ + void *dead = (void*)Bdead; + + if(b == nil) + return; + + /* + * drivers which perform non cache coherent DMA manage their own buffer + * pool of uncached buffers and provide their own free routine. + */ + if(b->free) { + b->free(b); + return; + } + if(b->flag & BINTR) { + ilock(&ialloc.lk); + ialloc.bytes -= b->lim - b->base; + iunlock(&ialloc.lk); + } + + /* poison the block in case someone is still holding onto it */ + b->next = dead; + b->rp = dead; + b->wp = dead; + b->lim = dead; + b->base = dead; + + free(b); +} + +void +checkb(Block *b, char *msg) +{ + void *dead = (void*)Bdead; + + if(b == dead) + panic("checkb b %s %lux", msg, b); + if(b->base == dead || b->lim == dead || b->next == dead + || b->rp == dead || b->wp == dead){ + print("checkb: base 0x%8.8luX lim 0x%8.8luX next 0x%8.8luX\n", + b->base, b->lim, b->next); + print("checkb: rp 0x%8.8luX wp 0x%8.8luX\n", b->rp, b->wp); + panic("checkb dead: %s\n", msg); + } + + if(b->base > b->lim) + panic("checkb 0 %s %lux %lux", msg, b->base, b->lim); + if(b->rp < b->base) + panic("checkb 1 %s %lux %lux", msg, b->base, b->rp); + if(b->wp < b->base) + panic("checkb 2 %s %lux %lux", msg, b->base, b->wp); + if(b->rp > b->lim) + panic("checkb 3 %s %lux %lux", msg, b->rp, b->lim); + if(b->wp > b->lim) + panic("checkb 4 %s %lux %lux", msg, b->wp, b->lim); + +} + +void +iallocsummary(void) +{ + print("ialloc %lud/%lud\n", ialloc.bytes, conf.ialloc); +} diff --git a/sys/src/cmd/unix/drawterm/kern/cache.c b/sys/src/cmd/unix/drawterm/kern/cache.c new file mode 100755 index 000000000..56ee23ca7 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/cache.c @@ -0,0 +1,46 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + + +void +cinit(void) +{ +} + +void +copen(Chan *c) +{ + USED(c); +} + +int +cread(Chan *c, uchar *buf, int len, vlong off) +{ + USED(c); + USED(buf); + USED(len); + USED(off); + + return 0; +} + +void +cupdate(Chan *c, uchar *buf, int len, vlong off) +{ + USED(c); + USED(buf); + USED(len); + USED(off); +} + +void +cwrite(Chan* c, uchar *buf, int len, vlong off) +{ + USED(c); + USED(buf); + USED(len); + USED(off); +} diff --git a/sys/src/cmd/unix/drawterm/kern/chan.c b/sys/src/cmd/unix/drawterm/kern/chan.c new file mode 100755 index 000000000..7105f3a69 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/chan.c @@ -0,0 +1,1494 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +int chandebug=0; /* toggled by sysr1 */ +QLock chanprint; /* probably asking for trouble (deadlocks) -rsc */ + +int domount(Chan**, Mhead**); + +void +dumpmount(void) /* DEBUGGING */ +{ + Pgrp *pg; + Mount *t; + Mhead **h, **he, *f; + + if(up == nil){ + print("no process for dumpmount\n"); + return; + } + pg = up->pgrp; + if(pg == nil){ + print("no pgrp for dumpmount\n"); + return; + } + rlock(&pg->ns); + if(waserror()) { + runlock(&pg->ns); + nexterror(); + } + + he = &pg->mnthash[MNTHASH]; + for(h = pg->mnthash; h < he; h++) { + for(f = *h; f; f = f->hash) { + print("head: %p: %s 0x%llux.%lud %C %lud -> \n", f, + f->from->name->s, f->from->qid.path, + f->from->qid.vers, devtab[f->from->type]->dc, + f->from->dev); + for(t = f->mount; t; t = t->next) + print("\t%p: %s (umh %p) (path %.8llux dev %C %lud)\n", t, t->to->name->s, t->to->umh, t->to->qid.path, devtab[t->to->type]->dc, t->to->dev); + } + } + poperror(); + runlock(&pg->ns); +} + + +char* +c2name(Chan *c) /* DEBUGGING */ +{ + if(c == nil) + return "<nil chan>"; + if(c->name == nil) + return "<nil name>"; + if(c->name->s == nil) + return "<nil name.s>"; + return c->name->s; +} + +enum +{ + CNAMESLOP = 20 +}; + +struct +{ + Lock lk; + int fid; + Chan *free; + Chan *list; +}chanalloc; + +typedef struct Elemlist Elemlist; + +struct Elemlist +{ + char *name; /* copy of name, so '/' can be overwritten */ + int nelems; + char **elems; + int *off; + int mustbedir; +}; + +#define SEP(c) ((c) == 0 || (c) == '/') +void cleancname(Cname*); + +int +isdotdot(char *p) +{ + return p[0]=='.' && p[1]=='.' && p[2]=='\0'; +} + +int +incref(Ref *r) +{ + int x; + + lock(&r->lk); + x = ++r->ref; + unlock(&r->lk); + return x; +} + +int +decref(Ref *r) +{ + int x; + + lock(&r->lk); + x = --r->ref; + unlock(&r->lk); + if(x < 0) + panic("decref, pc=0x%p", getcallerpc(&r)); + + return x; +} + +/* + * Rather than strncpy, which zeros the rest of the buffer, kstrcpy + * truncates if necessary, always zero terminates, does not zero fill, + * and puts ... at the end of the string if it's too long. Usually used to + * save a string in up->genbuf; + */ +void +kstrcpy(char *s, char *t, int ns) +{ + int nt; + + nt = strlen(t); + if(nt+1 <= ns){ + memmove(s, t, nt+1); + return; + } + /* too long */ + if(ns < 4){ + /* but very short! */ + strncpy(s, t, ns); + return; + } + /* truncate with ... at character boundary (very rare case) */ + memmove(s, t, ns-4); + ns -= 4; + s[ns] = '\0'; + /* look for first byte of UTF-8 sequence by skipping continuation bytes */ + while(ns>0 && (s[--ns]&0xC0)==0x80) + ; + strcpy(s+ns, "..."); +} + +int +emptystr(char *s) +{ + if(s == nil) + return 1; + if(s[0] == '\0') + return 1; + return 0; +} + +/* + * Atomically replace *p with copy of s + */ +void +kstrdup(char **p, char *s) +{ + int n; + char *t, *prev; + + n = strlen(s)+1; + /* if it's a user, we can wait for memory; if not, something's very wrong */ + if(up){ + t = smalloc(n); + setmalloctag(t, getcallerpc(&p)); + }else{ + t = malloc(n); + if(t == nil) + panic("kstrdup: no memory"); + } + memmove(t, s, n); + prev = *p; + *p = t; + free(prev); +} + +void +chandevreset(void) +{ + int i; + + for(i=0; devtab[i] != nil; i++) + devtab[i]->reset(); +} + +void +chandevinit(void) +{ + int i; + + for(i=0; devtab[i] != nil; i++) + devtab[i]->init(); +} + +void +chandevshutdown(void) +{ + int i; + + /* shutdown in reverse order */ + for(i=0; devtab[i] != nil; i++) + ; + for(i--; i >= 0; i--) + devtab[i]->shutdown(); +} + +Chan* +newchan(void) +{ + Chan *c; + + lock(&chanalloc.lk); + c = chanalloc.free; + if(c != 0) + chanalloc.free = c->next; + unlock(&chanalloc.lk); + + if(c == nil) { + c = smalloc(sizeof(Chan)); + lock(&chanalloc.lk); + c->fid = ++chanalloc.fid; + c->link = chanalloc.list; + chanalloc.list = c; + unlock(&chanalloc.lk); + } + + /* if you get an error before associating with a dev, + close calls rootclose, a nop */ + c->type = 0; + c->flag = 0; + c->ref.ref = 1; + c->dev = 0; + c->offset = 0; + c->iounit = 0; + c->umh = 0; + c->uri = 0; + c->dri = 0; + c->aux = 0; + c->mchan = 0; + c->mcp = 0; + c->mux = 0; + memset(&c->mqid, 0, sizeof(c->mqid)); + c->name = 0; + return c; +} + +static Ref ncname; + +Cname* +newcname(char *s) +{ + Cname *n; + int i; + + n = smalloc(sizeof(Cname)); + i = strlen(s); + n->len = i; + n->alen = i+CNAMESLOP; + n->s = smalloc(n->alen); + memmove(n->s, s, i+1); + n->ref.ref = 1; + incref(&ncname); + return n; +} + +void +cnameclose(Cname *n) +{ + if(n == nil) + return; + if(decref(&n->ref)) + return; + decref(&ncname); + free(n->s); + free(n); +} + +Cname* +addelem(Cname *n, char *s) +{ + int i, a; + char *t; + Cname *new; + + if(s[0]=='.' && s[1]=='\0') + return n; + + if(n->ref.ref > 1){ + /* copy on write */ + new = newcname(n->s); + cnameclose(n); + n = new; + } + + i = strlen(s); + if(n->len+1+i+1 > n->alen){ + a = n->len+1+i+1 + CNAMESLOP; + t = smalloc(a); + memmove(t, n->s, n->len+1); + free(n->s); + n->s = t; + n->alen = a; + } + if(n->len>0 && n->s[n->len-1]!='/' && s[0]!='/') /* don't insert extra slash if one is present */ + n->s[n->len++] = '/'; + memmove(n->s+n->len, s, i+1); + n->len += i; + if(isdotdot(s)) + cleancname(n); + return n; +} + +void +chanfree(Chan *c) +{ + c->flag = CFREE; + + if(c->umh != nil){ + putmhead(c->umh); + c->umh = nil; + } + if(c->umc != nil){ + cclose(c->umc); + c->umc = nil; + } + if(c->mux != nil){ + muxclose(c->mux); + c->mux = nil; + } + if(c->mchan != nil){ + cclose(c->mchan); + c->mchan = nil; + } + + cnameclose(c->name); + + lock(&chanalloc.lk); + c->next = chanalloc.free; + chanalloc.free = c; + unlock(&chanalloc.lk); +} + +void +cclose(Chan *c) +{ + if(c->flag&CFREE) + panic("cclose %p", getcallerpc(&c)); + + if(decref(&c->ref)) + return; + + if(!waserror()){ + devtab[c->type]->close(c); + poperror(); + } + chanfree(c); +} + +/* + * Make sure we have the only copy of c. (Copy on write.) + */ +Chan* +cunique(Chan *c) +{ + Chan *nc; + + if(c->ref.ref != 1) { + nc = cclone(c); + cclose(c); + c = nc; + } + + return c; +} + +int +eqqid(Qid a, Qid b) +{ + return a.path==b.path && a.vers==b.vers; +} + +int +eqchan(Chan *a, Chan *b, int pathonly) +{ + if(a->qid.path != b->qid.path) + return 0; + if(!pathonly && a->qid.vers!=b->qid.vers) + return 0; + if(a->type != b->type) + return 0; + if(a->dev != b->dev) + return 0; + return 1; +} + +int +eqchantdqid(Chan *a, int type, int dev, Qid qid, int pathonly) +{ + if(a->qid.path != qid.path) + return 0; + if(!pathonly && a->qid.vers!=qid.vers) + return 0; + if(a->type != type) + return 0; + if(a->dev != dev) + return 0; + return 1; +} + +Mhead* +newmhead(Chan *from) +{ + Mhead *mh; + + mh = smalloc(sizeof(Mhead)); + mh->ref.ref = 1; + mh->from = from; + incref(&from->ref); + +/* + n = from->name->len; + if(n >= sizeof(mh->fromname)) + n = sizeof(mh->fromname)-1; + memmove(mh->fromname, from->name->s, n); + mh->fromname[n] = 0; +*/ + return mh; +} + +int +cmount(Chan **newp, Chan *old, int flag, char *spec) +{ + Pgrp *pg; + int order, flg; + Mhead *m, **l, *mh; + Mount *nm, *f, *um, **h; + Chan *new; + + if(QTDIR & (old->qid.type^(*newp)->qid.type)) + error(Emount); + +if(old->umh)print("cmount old extra umh\n"); + + order = flag&MORDER; + + if((old->qid.type&QTDIR)==0 && order != MREPL) + error(Emount); + + new = *newp; + mh = new->umh; + + /* + * Not allowed to bind when the old directory + * is itself a union. (Maybe it should be allowed, but I don't see + * what the semantics would be.) + * + * We need to check mh->mount->next to tell unions apart from + * simple mount points, so that things like + * mount -c fd /root + * bind -c /root / + * work. The check of mount->mflag catches things like + * mount fd /root + * bind -c /root / + * + * This is far more complicated than it should be, but I don't + * see an easier way at the moment. -rsc + */ + if((flag&MCREATE) && mh && mh->mount + && (mh->mount->next || !(mh->mount->mflag&MCREATE))) + error(Emount); + + pg = up->pgrp; + wlock(&pg->ns); + + l = &MOUNTH(pg, old->qid); + for(m = *l; m; m = m->hash) { + if(eqchan(m->from, old, 1)) + break; + l = &m->hash; + } + + if(m == nil) { + /* + * nothing mounted here yet. create a mount + * head and add to the hash table. + */ + m = newmhead(old); + *l = m; + + /* + * if this is a union mount, add the old + * node to the mount chain. + */ + if(order != MREPL) + m->mount = newmount(m, old, 0, 0); + } + wlock(&m->lock); + if(waserror()){ + wunlock(&m->lock); + nexterror(); + } + wunlock(&pg->ns); + + nm = newmount(m, new, flag, spec); + if(mh != nil && mh->mount != nil) { + /* + * copy a union when binding it onto a directory + */ + flg = order; + if(order == MREPL) + flg = MAFTER; + h = &nm->next; + um = mh->mount; + for(um = um->next; um; um = um->next) { + f = newmount(m, um->to, flg, um->spec); + *h = f; + h = &f->next; + } + } + + if(m->mount && order == MREPL) { + mountfree(m->mount); + m->mount = 0; + } + + if(flag & MCREATE) + nm->mflag |= MCREATE; + + if(m->mount && order == MAFTER) { + for(f = m->mount; f->next; f = f->next) + ; + f->next = nm; + } + else { + for(f = nm; f->next; f = f->next) + ; + f->next = m->mount; + m->mount = nm; + } + + wunlock(&m->lock); + poperror(); + return nm->mountid; +} + +void +cunmount(Chan *mnt, Chan *mounted) +{ + Pgrp *pg; + Mhead *m, **l; + Mount *f, **p; + + if(mnt->umh) /* should not happen */ + print("cunmount newp extra umh %p has %p\n", mnt, mnt->umh); + + /* + * It _can_ happen that mounted->umh is non-nil, + * because mounted is the result of namec(Aopen) + * (see sysfile.c:/^sysunmount). + * If we open a union directory, it will have a umh. + * Although surprising, this is okay, since the + * cclose will take care of freeing the umh. + */ + + pg = up->pgrp; + wlock(&pg->ns); + + l = &MOUNTH(pg, mnt->qid); + for(m = *l; m; m = m->hash) { + if(eqchan(m->from, mnt, 1)) + break; + l = &m->hash; + } + + if(m == 0) { + wunlock(&pg->ns); + error(Eunmount); + } + + wlock(&m->lock); + if(mounted == 0) { + *l = m->hash; + wunlock(&pg->ns); + mountfree(m->mount); + m->mount = nil; + cclose(m->from); + wunlock(&m->lock); + putmhead(m); + return; + } + + p = &m->mount; + for(f = *p; f; f = f->next) { + /* BUG: Needs to be 2 pass */ + if(eqchan(f->to, mounted, 1) || + (f->to->mchan && eqchan(f->to->mchan, mounted, 1))) { + *p = f->next; + f->next = 0; + mountfree(f); + if(m->mount == nil) { + *l = m->hash; + cclose(m->from); + wunlock(&m->lock); + wunlock(&pg->ns); + putmhead(m); + return; + } + wunlock(&m->lock); + wunlock(&pg->ns); + return; + } + p = &f->next; + } + wunlock(&m->lock); + wunlock(&pg->ns); + error(Eunion); +} + +Chan* +cclone(Chan *c) +{ + Chan *nc; + Walkqid *wq; + + wq = devtab[c->type]->walk(c, nil, nil, 0); + if(wq == nil) + error("clone failed"); + nc = wq->clone; + free(wq); + nc->name = c->name; + if(c->name) + incref(&c->name->ref); + return nc; +} + +int +findmount(Chan **cp, Mhead **mp, int type, int dev, Qid qid) +{ + Pgrp *pg; + Mhead *m; + + pg = up->pgrp; + rlock(&pg->ns); + for(m = MOUNTH(pg, qid); m; m = m->hash){ + rlock(&m->lock); +if(m->from == nil){ + print("m %p m->from 0\n", m); + runlock(&m->lock); + continue; +} + if(eqchantdqid(m->from, type, dev, qid, 1)) { + runlock(&pg->ns); + if(mp != nil){ + incref(&m->ref); + if(*mp != nil) + putmhead(*mp); + *mp = m; + } + if(*cp != nil) + cclose(*cp); + incref(&m->mount->to->ref); + *cp = m->mount->to; + runlock(&m->lock); + return 1; + } + runlock(&m->lock); + } + + runlock(&pg->ns); + return 0; +} + +int +domount(Chan **cp, Mhead **mp) +{ + return findmount(cp, mp, (*cp)->type, (*cp)->dev, (*cp)->qid); +} + +Chan* +undomount(Chan *c, Cname *name) +{ + Chan *nc; + Pgrp *pg; + Mount *t; + Mhead **h, **he, *f; + + pg = up->pgrp; + rlock(&pg->ns); + if(waserror()) { + runlock(&pg->ns); + nexterror(); + } + + he = &pg->mnthash[MNTHASH]; + for(h = pg->mnthash; h < he; h++) { + for(f = *h; f; f = f->hash) { + if(strcmp(f->from->name->s, name->s) != 0) + continue; + for(t = f->mount; t; t = t->next) { + if(eqchan(c, t->to, 1)) { + /* + * We want to come out on the left hand side of the mount + * point using the element of the union that we entered on. + * To do this, find the element that has a from name of + * c->name->s. + */ + if(strcmp(t->head->from->name->s, name->s) != 0) + continue; + nc = t->head->from; + incref(&nc->ref); + cclose(c); + c = nc; + break; + } + } + } + } + poperror(); + runlock(&pg->ns); + return c; +} + +/* + * Either walks all the way or not at all. No partial results in *cp. + * *nerror is the number of names to display in an error message. + */ +static char Edoesnotexist[] = "does not exist"; +int +walk(Chan **cp, char **names, int nnames, int nomount, int *nerror) +{ + int dev, dotdot, i, n, nhave, ntry, type; + Chan *c, *nc; + Cname *cname; + Mount *f; + Mhead *mh, *nmh; + Walkqid *wq; + + c = *cp; + incref(&c->ref); + cname = c->name; + incref(&cname->ref); + mh = nil; + + /* + * While we haven't gotten all the way down the path: + * 1. step through a mount point, if any + * 2. send a walk request for initial dotdot or initial prefix without dotdot + * 3. move to the first mountpoint along the way. + * 4. repeat. + * + * An invariant is that each time through the loop, c is on the undomount + * side of the mount point, and c's name is cname. + */ + for(nhave=0; nhave<nnames; nhave+=n){ + if((c->qid.type&QTDIR)==0){ + if(nerror) + *nerror = nhave; + cnameclose(cname); + cclose(c); + strcpy(up->errstr, Enotdir); + if(mh != nil) +{print("walk 1\n"); + putmhead(mh); +} + return -1; + } + ntry = nnames - nhave; + if(ntry > MAXWELEM) + ntry = MAXWELEM; + dotdot = 0; + for(i=0; i<ntry; i++){ + if(isdotdot(names[nhave+i])){ + if(i==0) { + dotdot = 1; + ntry = 1; + } else + ntry = i; + break; + } + } + + if(!dotdot && !nomount) + domount(&c, &mh); + + type = c->type; + dev = c->dev; + + if((wq = devtab[type]->walk(c, nil, names+nhave, ntry)) == nil){ + /* try a union mount, if any */ + if(mh && !nomount){ + /* + * mh->mount == c, so start at mh->mount->next + */ + rlock(&mh->lock); + for(f = mh->mount->next; f; f = f->next) + if((wq = devtab[f->to->type]->walk(f->to, nil, names+nhave, ntry)) != nil) + break; + runlock(&mh->lock); + if(f != nil){ + type = f->to->type; + dev = f->to->dev; + } + } + if(wq == nil){ + cclose(c); + cnameclose(cname); + if(nerror) + *nerror = nhave+1; + if(mh != nil) + putmhead(mh); + return -1; + } + } + + nmh = nil; + if(dotdot) { + assert(wq->nqid == 1); + assert(wq->clone != nil); + + cname = addelem(cname, ".."); + nc = undomount(wq->clone, cname); + n = 1; + } else { + nc = nil; + if(!nomount) + for(i=0; i<wq->nqid && i<ntry-1; i++) + if(findmount(&nc, &nmh, type, dev, wq->qid[i])) + break; + if(nc == nil){ /* no mount points along path */ + if(wq->clone == nil){ + cclose(c); + cnameclose(cname); + if(wq->nqid==0 || (wq->qid[wq->nqid-1].type&QTDIR)){ + if(nerror) + *nerror = nhave+wq->nqid+1; + strcpy(up->errstr, Edoesnotexist); + }else{ + if(nerror) + *nerror = nhave+wq->nqid; + strcpy(up->errstr, Enotdir); + } + free(wq); + if(mh != nil) + putmhead(mh); + return -1; + } + n = wq->nqid; + nc = wq->clone; + }else{ /* stopped early, at a mount point */ + if(wq->clone != nil){ + cclose(wq->clone); + wq->clone = nil; + } + n = i+1; + } + for(i=0; i<n; i++) + cname = addelem(cname, names[nhave+i]); + } + cclose(c); + c = nc; + putmhead(mh); + mh = nmh; + free(wq); + } + + putmhead(mh); + + c = cunique(c); + + if(c->umh != nil){ //BUG + print("walk umh\n"); + putmhead(c->umh); + c->umh = nil; + } + + cnameclose(c->name); + c->name = cname; + + cclose(*cp); + *cp = c; + if(nerror) + *nerror = 0; + return 0; +} + +/* + * c is a mounted non-creatable directory. find a creatable one. + */ +Chan* +createdir(Chan *c, Mhead *m) +{ + Chan *nc; + Mount *f; + + rlock(&m->lock); + if(waserror()) { + runlock(&m->lock); + nexterror(); + } + for(f = m->mount; f; f = f->next) { + if(f->mflag&MCREATE) { + nc = cclone(f->to); + runlock(&m->lock); + poperror(); + cclose(c); + return nc; + } + } + error(Enocreate); + return 0; +} + +void +saveregisters(void) +{ +} + +/* + * In place, rewrite name to compress multiple /, eliminate ., and process .. + */ +void +cleancname(Cname *n) +{ + char *p; + + if(n->s[0] == '#'){ + p = strchr(n->s, '/'); + if(p == nil) + return; + cleanname(p); + + /* + * The correct name is #i rather than #i/, + * but the correct name of #/ is #/. + */ + if(strcmp(p, "/")==0 && n->s[1] != '/') + *p = '\0'; + }else + cleanname(n->s); + n->len = strlen(n->s); +} + +static void +growparse(Elemlist *e) +{ + char **new; + int *inew; + enum { Delta = 8 }; + + if(e->nelems % Delta == 0){ + new = smalloc((e->nelems+Delta) * sizeof(char*)); + memmove(new, e->elems, e->nelems*sizeof(char*)); + free(e->elems); + e->elems = new; + inew = smalloc((e->nelems+Delta+1) * sizeof(int)); + memmove(inew, e->off, e->nelems*sizeof(int)); + free(e->off); + e->off = inew; + } +} + +/* + * The name is known to be valid. + * Copy the name so slashes can be overwritten. + * An empty string will set nelem=0. + * A path ending in / or /. or /.//./ etc. will have + * e.mustbedir = 1, so that we correctly + * reject, e.g., "/adm/users/." when /adm/users is a file + * rather than a directory. + */ +static void +parsename(char *name, Elemlist *e) +{ + char *slash; + + kstrdup(&e->name, name); + name = e->name; + e->nelems = 0; + e->elems = nil; + e->off = smalloc(sizeof(int)); + e->off[0] = skipslash(name) - name; + for(;;){ + name = skipslash(name); + if(*name=='\0'){ + e->mustbedir = 1; + break; + } + growparse(e); + e->elems[e->nelems++] = name; + slash = utfrune(name, '/'); + if(slash == nil){ + e->off[e->nelems] = name+strlen(name) - e->name; + e->mustbedir = 0; + break; + } + e->off[e->nelems] = slash - e->name; + *slash++ = '\0'; + name = slash; + } +} + +void* +mymemrchr(void *va, int c, long n) +{ + uchar *a, *e; + + a = va; + for(e=a+n-1; e>a; e--) + if(*e == c) + return e; + return nil; +} + +/* + * Turn a name into a channel. + * &name[0] is known to be a valid address. It may be a kernel address. + * + * Opening with amode Aopen, Acreate, or Aremove guarantees + * that the result will be the only reference to that particular fid. + * This is necessary since we might pass the result to + * devtab[]->remove(). + * + * Opening Atodir, Amount, or Aaccess does not guarantee this. + * + * Opening Aaccess can, under certain conditions, return a + * correct Chan* but with an incorrect Cname attached. + * Since the functions that open Aaccess (sysstat, syswstat, sys_stat) + * do not use the Cname*, this avoids an unnecessary clone. + */ +Chan* +namec(char *aname, int amode, int omode, ulong perm) +{ + int n, prefix, len, t, nomount, npath; + Chan *c, *cnew; + Cname *cname; + Elemlist e; + Rune r; + Mhead *m; + char *createerr, tmperrbuf[ERRMAX]; + char *name; + + name = aname; + if(name[0] == '\0') + error("empty file name"); + validname(name, 1); + + /* + * Find the starting off point (the current slash, the root of + * a device tree, or the current dot) as well as the name to + * evaluate starting there. + */ + nomount = 0; + switch(name[0]){ + case '/': + c = up->slash; + incref(&c->ref); + break; + + case '#': + nomount = 1; + up->genbuf[0] = '\0'; + n = 0; + while(*name!='\0' && (*name != '/' || n < 2)){ + if(n >= sizeof(up->genbuf)-1) + error(Efilename); + up->genbuf[n++] = *name++; + } + up->genbuf[n] = '\0'; + /* + * noattach is sandboxing. + * + * the OK exceptions are: + * | it only gives access to pipes you create + * d this process's file descriptors + * e this process's environment + * the iffy exceptions are: + * c time and pid, but also cons and consctl + * p control of your own processes (and unfortunately + * any others left unprotected) + */ + n = chartorune(&r, up->genbuf+1)+1; + /* actually / is caught by parsing earlier */ + if(utfrune("M", r)) + error(Enoattach); + if(up->pgrp->noattach && utfrune("|decp", r)==nil) + error(Enoattach); + t = devno(r, 1); + if(t == -1) + error(Ebadsharp); + c = devtab[t]->attach(up->genbuf+n); + break; + + default: + c = up->dot; + incref(&c->ref); + break; + } + prefix = name - aname; + + e.name = nil; + e.elems = nil; + e.off = nil; + e.nelems = 0; + if(waserror()){ + cclose(c); + free(e.name); + free(e.elems); + free(e.off); +//dumpmount(); + nexterror(); + } + + /* + * Build a list of elements in the path. + */ + parsename(name, &e); + + /* + * On create, .... + */ + if(amode == Acreate){ + /* perm must have DMDIR if last element is / or /. */ + if(e.mustbedir && !(perm&DMDIR)){ + npath = e.nelems; + strcpy(tmperrbuf, "create without DMDIR"); + goto NameError; + } + + /* don't try to walk the last path element just yet. */ + if(e.nelems == 0) + error(Eexist); + e.nelems--; + } + + if(walk(&c, e.elems, e.nelems, nomount, &npath) < 0){ + if(npath < 0 || npath > e.nelems){ + print("namec %s walk error npath=%d\n", aname, npath); + nexterror(); + } + strcpy(tmperrbuf, up->errstr); + NameError: + len = prefix+e.off[npath]; + if(len < ERRMAX/3 || (name=mymemrchr(aname, '/', len))==nil || name==aname) + snprint(up->genbuf, sizeof up->genbuf, "%.*s", len, aname); + else + snprint(up->genbuf, sizeof up->genbuf, "...%.*s", (int)(len-(name-aname)), name); + snprint(up->errstr, ERRMAX, "%#q %s", up->genbuf, tmperrbuf); + nexterror(); + } + + if(e.mustbedir && !(c->qid.type&QTDIR)){ + npath = e.nelems; + strcpy(tmperrbuf, "not a directory"); + goto NameError; + } + + if(amode == Aopen && (omode&3) == OEXEC && (c->qid.type&QTDIR)){ + npath = e.nelems; + error("cannot exec directory"); + } + + switch(amode){ + case Aaccess: + if(!nomount) + domount(&c, nil); + break; + + case Abind: + m = nil; + if(!nomount) + domount(&c, &m); + if(c->umh != nil) + putmhead(c->umh); + c->umh = m; + break; + + case Aremove: + case Aopen: + Open: + /* save the name; domount might change c */ + cname = c->name; + incref(&cname->ref); + m = nil; + if(!nomount) + domount(&c, &m); + + /* our own copy to open or remove */ + c = cunique(c); + + /* now it's our copy anyway, we can put the name back */ + cnameclose(c->name); + c->name = cname; + + switch(amode){ + case Aremove: + putmhead(m); + break; + + case Aopen: + case Acreate: +if(c->umh != nil){ + print("cunique umh Open\n"); + putmhead(c->umh); + c->umh = nil; +} + + /* only save the mount head if it's a multiple element union */ + if(m && m->mount && m->mount->next) + c->umh = m; + else + putmhead(m); + + /* save registers else error() in open has wrong value of c saved */ + saveregisters(); + + if(omode == OEXEC) + c->flag &= ~CCACHE; + + c = devtab[c->type]->open(c, omode&~OCEXEC); + + if(omode & OCEXEC) + c->flag |= CCEXEC; + if(omode & ORCLOSE) + c->flag |= CRCLOSE; + break; + } + break; + + case Atodir: + /* + * Directories (e.g. for cd) are left before the mount point, + * so one may mount on / or . and see the effect. + */ + if(!(c->qid.type & QTDIR)) + error(Enotdir); + break; + + case Amount: + /* + * When mounting on an already mounted upon directory, + * one wants subsequent mounts to be attached to the + * original directory, not the replacement. Don't domount. + */ + break; + + case Acreate: + /* + * We've already walked all but the last element. + * If the last exists, try to open it OTRUNC. + * If omode&OEXCL is set, just give up. + */ + e.nelems++; + if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) == 0){ + if(omode&OEXCL) + error(Eexist); + omode |= OTRUNC; + goto Open; + } + + /* + * The semantics of the create(2) system call are that if the + * file exists and can be written, it is to be opened with truncation. + * On the other hand, the create(5) message fails if the file exists. + * If we get two create(2) calls happening simultaneously, + * they might both get here and send create(5) messages, but only + * one of the messages will succeed. To provide the expected create(2) + * semantics, the call with the failed message needs to try the above + * walk again, opening for truncation. This correctly solves the + * create/create race, in the sense that any observable outcome can + * be explained as one happening before the other. + * The create/create race is quite common. For example, it happens + * when two rc subshells simultaneously update the same + * environment variable. + * + * The implementation still admits a create/create/remove race: + * (A) walk to file, fails + * (B) walk to file, fails + * (A) create file, succeeds, returns + * (B) create file, fails + * (A) remove file, succeeds, returns + * (B) walk to file, return failure. + * + * This is hardly as common as the create/create race, and is really + * not too much worse than what might happen if (B) got a hold of a + * file descriptor and then the file was removed -- either way (B) can't do + * anything with the result of the create call. So we don't care about this race. + * + * Applications that care about more fine-grained decision of the races + * can use the OEXCL flag to get at the underlying create(5) semantics; + * by default we provide the common case. + * + * We need to stay behind the mount point in case we + * need to do the first walk again (should the create fail). + * + * We also need to cross the mount point and find the directory + * in the union in which we should be creating. + * + * The channel staying behind is c, the one moving forward is cnew. + */ + m = nil; + cnew = nil; /* is this assignment necessary? */ + if(!waserror()){ /* try create */ + if(!nomount && findmount(&cnew, &m, c->type, c->dev, c->qid)) + cnew = createdir(cnew, m); + else{ + cnew = c; + incref(&cnew->ref); + } + + /* + * We need our own copy of the Chan because we're + * about to send a create, which will move it. Once we have + * our own copy, we can fix the name, which might be wrong + * if findmount gave us a new Chan. + */ + cnew = cunique(cnew); + cnameclose(cnew->name); + cnew->name = c->name; + incref(&cnew->name->ref); + + devtab[cnew->type]->create(cnew, e.elems[e.nelems-1], omode&~(OEXCL|OCEXEC), perm); + poperror(); + if(omode & OCEXEC) + cnew->flag |= CCEXEC; + if(omode & ORCLOSE) + cnew->flag |= CRCLOSE; + if(m) + putmhead(m); + cclose(c); + c = cnew; + c->name = addelem(c->name, e.elems[e.nelems-1]); + break; + }else{ /* create failed */ + cclose(cnew); + if(m) + putmhead(m); + if(omode & OEXCL) + nexterror(); + /* save error */ + createerr = up->errstr; + up->errstr = tmperrbuf; + /* note: we depend that walk does not error */ + if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) < 0){ + up->errstr = createerr; + error(createerr); /* report true error */ + } + up->errstr = createerr; + omode |= OTRUNC; + goto Open; + } + panic("namec: not reached"); + + default: + panic("unknown namec access %d\n", amode); + } + + poperror(); + + /* place final element in genbuf for e.g. exec */ + if(e.nelems > 0) + kstrcpy(up->genbuf, e.elems[e.nelems-1], sizeof up->genbuf); + else + kstrcpy(up->genbuf, ".", sizeof up->genbuf); + free(e.name); + free(e.elems); + free(e.off); + + return c; +} + +/* + * name is valid. skip leading / and ./ as much as possible + */ +char* +skipslash(char *name) +{ + while(name[0]=='/' || (name[0]=='.' && (name[1]==0 || name[1]=='/'))) + name++; + return name; +} + +char isfrog[256]={ + /*NUL*/ 1, 1, 1, 1, 1, 1, 1, 1, /* 0 */ + /*BKS*/ 1, 1, 1, 1, 1, 1, 1, 1, /* 0x08 */ + /*DLE*/ 1, 1, 1, 1, 1, 1, 1, 1, /* 0x10 */ + /*CAN*/ 1, 1, 1, 1, 1, 1, 1, 1, /* 0x18 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 */ + 0, 0, 0, 0, 0, 0, 0, 1, /* 0x28 (1 is '/', 0x2F) */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x38 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x48 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x58 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x68 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 */ + 0, 0, 0, 0, 0, 0, 0, 1, /* 0x78 (1 is DEL, 0x7F) */ +}; + +/* + * Check that the name + * a) is in valid memory. + * b) is shorter than 2^16 bytes, so it can fit in a 9P string field. + * c) contains no frogs. + * The first byte is known to be addressible by the requester, so the + * routine works for kernel and user memory both. + * The parameter slashok flags whether a slash character is an error + * or a valid character. + */ +void +validname(char *aname, int slashok) +{ + char *ename, *name; + int c; + Rune r; + + name = aname; +/* + if(((ulong)name & KZERO) != KZERO) { + p = name; + t = BY2PG-((ulong)p&(BY2PG-1)); + while((ename=vmemchr(p, 0, t)) == nil) { + p += t; + t = BY2PG; + } + }else +*/ + ename = memchr(name, 0, (1<<16)); + + if(ename==nil || ename-name>=(1<<16)) + error("name too long"); + + while(*name){ + /* all characters above '~' are ok */ + c = *(uchar*)name; + if(c >= Runeself) + name += chartorune(&r, name); + else{ + if(isfrog[c]) + if(!slashok || c!='/'){ + snprint(up->genbuf, sizeof(up->genbuf), "%s: %q", Ebadchar, aname); + error(up->genbuf); + } + name++; + } + } +} + +void +isdir(Chan *c) +{ + if(c->qid.type & QTDIR) + return; + error(Enotdir); +} + +/* + * This is necessary because there are many + * pointers to the top of a given mount list: + * + * - the mhead in the namespace hash table + * - the mhead in chans returned from findmount: + * used in namec and then by unionread. + * - the mhead in chans returned from createdir: + * used in the open/create race protect, which is gone. + * + * The RWlock in the Mhead protects the mount list it contains. + * The mount list is deleted when we cunmount. + * The RWlock ensures that nothing is using the mount list at that time. + * + * It is okay to replace c->mh with whatever you want as + * long as you are sure you have a unique reference to it. + * + * This comment might belong somewhere else. + */ +void +putmhead(Mhead *m) +{ + if(m && decref(&m->ref) == 0){ + m->mount = (Mount*)0xCafeBeef; + free(m); + } +} diff --git a/sys/src/cmd/unix/drawterm/kern/dat.h b/sys/src/cmd/unix/drawterm/kern/dat.h new file mode 100755 index 000000000..9d00d0085 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/dat.h @@ -0,0 +1,519 @@ +#define KNAMELEN 28 /* max length of name held in kernel */ +#define DOMLEN 64 + +#define BLOCKALIGN 8 + +typedef struct Alarms Alarms; +typedef struct Block Block; +typedef struct CSN CSN; +typedef struct Chan Chan; +typedef struct Cmdbuf Cmdbuf; +typedef struct Cmdtab Cmdtab; +typedef struct Cname Cname; +typedef struct Conf Conf; +typedef struct Dev Dev; +typedef struct Dirtab Dirtab; +typedef struct Edfinterface Edfinterface; +typedef struct Egrp Egrp; +typedef struct Evalue Evalue; +typedef struct Fgrp Fgrp; +typedef struct FPsave FPsave; +typedef struct DevConf DevConf; +typedef struct Label Label; +typedef struct List List; +typedef struct Log Log; +typedef struct Logflag Logflag; +typedef struct Mntcache Mntcache; +typedef struct Mount Mount; +typedef struct Mntrpc Mntrpc; +typedef struct Mntwalk Mntwalk; +typedef struct Mnt Mnt; +typedef struct Mhead Mhead; +typedef struct Note Note; +typedef struct Page Page; +typedef struct Palloc Palloc; +typedef struct Perf Perf; +typedef struct Pgrps Pgrps; +typedef struct PhysUart PhysUart; +typedef struct Pgrp Pgrp; +typedef struct Physseg Physseg; +typedef struct Proc Proc; +typedef struct Pte Pte; +typedef struct Pthash Pthash; +typedef struct Queue Queue; +typedef struct Ref Ref; +typedef struct Rendez Rendez; +typedef struct Rgrp Rgrp; +typedef struct RWlock RWlock; +typedef struct Schedq Schedq; +typedef struct Segment Segment; +typedef struct Session Session; +typedef struct Task Task; +typedef struct Talarm Talarm; +typedef struct Timer Timer; +typedef struct Uart Uart; +typedef struct Ureg Ureg; +typedef struct Waitq Waitq; +typedef struct Walkqid Walkqid; +typedef int Devgen(Chan*, char*, Dirtab*, int, int, Dir*); + +#include "fcall.h" + +enum +{ + SnarfSize = 64*1024, +}; + +struct Conf +{ + ulong nmach; /* processors */ + ulong nproc; /* processes */ + ulong monitor; /* has monitor? */ + ulong npage0; /* total physical pages of memory */ + ulong npage1; /* total physical pages of memory */ + ulong npage; /* total physical pages of memory */ + ulong upages; /* user page pool */ + ulong nimage; /* number of page cache image headers */ + ulong nswap; /* number of swap pages */ + int nswppo; /* max # of pageouts per segment pass */ + ulong base0; /* base of bank 0 */ + ulong base1; /* base of bank 1 */ + ulong copymode; /* 0 is copy on write, 1 is copy on reference */ + ulong ialloc; /* max interrupt time allocation in bytes */ + ulong pipeqsize; /* size in bytes of pipe queues */ + int nuart; /* number of uart devices */ +}; + +struct Label +{ + jmp_buf buf; +}; + +struct Ref +{ + Lock lk; + long ref; +}; + +struct Rendez +{ + Lock lk; + Proc *p; +}; + +struct RWlock /* changed from kernel */ +{ + int readers; + Lock lk; + QLock x; + QLock k; +}; + +struct Talarm +{ + Lock lk; + Proc *list; +}; + +struct Alarms +{ + QLock lk; + Proc *head; +}; + +/* + * Access types in namec & channel flags + */ +enum +{ + Aaccess, /* as in stat, wstat */ + Abind, /* for left-hand-side of bind */ + Atodir, /* as in chdir */ + Aopen, /* for i/o */ + Amount, /* to be mounted or mounted upon */ + Acreate, /* is to be created */ + Aremove, /* will be removed by caller */ + + COPEN = 0x0001, /* for i/o */ + CMSG = 0x0002, /* the message channel for a mount */ +/* CCREATE = 0x0004, permits creation if c->mnt */ + CCEXEC = 0x0008, /* close on exec */ + CFREE = 0x0010, /* not in use */ + CRCLOSE = 0x0020, /* remove on close */ + CCACHE = 0x0080, /* client cache */ +}; + +/* flag values */ +enum +{ + BINTR = (1<<0), + BFREE = (1<<1), + Bipck = (1<<2), /* ip checksum */ + Budpck = (1<<3), /* udp checksum */ + Btcpck = (1<<4), /* tcp checksum */ + Bpktck = (1<<5), /* packet checksum */ +}; + +struct Block +{ + Block* next; + Block* list; + uchar* rp; /* first unconsumed byte */ + uchar* wp; /* first empty byte */ + uchar* lim; /* 1 past the end of the buffer */ + uchar* base; /* start of the buffer */ + void (*free)(Block*); + ushort flag; + ushort checksum; /* IP checksum of complete packet (minus media header) */ +}; +#define BLEN(s) ((s)->wp - (s)->rp) +#define BALLOC(s) ((s)->lim - (s)->base) + +struct Chan +{ + Ref ref; + Chan* next; /* allocation */ + Chan* link; + vlong offset; /* in file */ + ushort type; + ulong dev; + ushort mode; /* read/write */ + ushort flag; + Qid qid; + int fid; /* for devmnt */ + ulong iounit; /* chunk size for i/o; 0==default */ + Mhead* umh; /* mount point that derived Chan; used in unionread */ + Chan* umc; /* channel in union; held for union read */ + QLock umqlock; /* serialize unionreads */ + int uri; /* union read index */ + int dri; /* devdirread index */ + ulong mountid; + Mntcache *mcp; /* Mount cache pointer */ + Mnt *mux; /* Mnt for clients using me for messages */ + void* aux; + Qid pgrpid; /* for #p/notepg */ + ulong mid; /* for ns in devproc */ + Chan* mchan; /* channel to mounted server */ + Qid mqid; /* qid of root of mount point */ + Session*session; + Cname *name; +}; + +struct Cname +{ + Ref ref; + int alen; /* allocated length */ + int len; /* strlen(s) */ + char *s; +}; + +struct Dev +{ + int dc; + char* name; + + void (*reset)(void); + void (*init)(void); + void (*shutdown)(void); + Chan* (*attach)(char*); + Walkqid* (*walk)(Chan*, Chan*, char**, int); + int (*stat)(Chan*, uchar*, int); + Chan* (*open)(Chan*, int); + void (*create)(Chan*, char*, int, ulong); + void (*close)(Chan*); + long (*read)(Chan*, void*, long, vlong); + Block* (*bread)(Chan*, long, ulong); + long (*write)(Chan*, void*, long, vlong); + long (*bwrite)(Chan*, Block*, ulong); + void (*remove)(Chan*); + int (*wstat)(Chan*, uchar*, int); + void (*power)(int); /* power mgt: power(1) => on, power (0) => off */ + int (*config)(int, char*, DevConf*); // returns nil on error +}; + +struct Dirtab +{ + char name[KNAMELEN]; + Qid qid; + vlong length; + ulong perm; +}; + +struct Walkqid +{ + Chan *clone; + int nqid; + Qid qid[1]; +}; + +enum +{ + NSMAX = 1000, + NSLOG = 7, + NSCACHE = (1<<NSLOG), +}; + +struct Mntwalk /* state for /proc/#/ns */ +{ + int cddone; + ulong id; + Mhead* mh; + Mount* cm; +}; + +struct Mount +{ + ulong mountid; + Mount* next; + Mhead* head; + Mount* copy; + Mount* order; + Chan* to; /* channel replacing channel */ + int mflag; + char *spec; +}; + +struct Mhead +{ + Ref ref; + RWlock lock; + Chan* from; /* channel mounted upon */ + Mount* mount; /* what's mounted upon it */ + Mhead* hash; /* Hash chain */ +}; + +struct Mnt +{ + Lock lk; + /* references are counted using c->ref; channels on this mount point incref(c->mchan) == Mnt.c */ + Chan *c; /* Channel to file service */ + Proc *rip; /* Reader in progress */ + Mntrpc *queue; /* Queue of pending requests on this channel */ + ulong id; /* Multiplexer id for channel check */ + Mnt *list; /* Free list */ + int flags; /* cache */ + int msize; /* data + IOHDRSZ */ + char *version; /* 9P version */ + Queue *q; /* input queue */ +}; + +enum +{ + NUser, /* note provided externally */ + NExit, /* deliver note quietly */ + NDebug, /* print debug message */ +}; + +struct Note +{ + char msg[ERRMAX]; + int flag; /* whether system posted it */ +}; + +enum +{ + RENDLOG = 5, + RENDHASH = 1<<RENDLOG, /* Hash to lookup rendezvous tags */ + MNTLOG = 5, + MNTHASH = 1<<MNTLOG, /* Hash to walk mount table */ + NFD = 100, /* per process file descriptors */ + PGHLOG = 9, + PGHSIZE = 1<<PGHLOG, /* Page hash for image lookup */ +}; +#define REND(p,s) ((p)->rendhash[(s)&((1<<RENDLOG)-1)]) +#define MOUNTH(p,qid) ((p)->mnthash[(qid).path&((1<<MNTLOG)-1)]) + +struct Pgrp +{ + Ref ref; /* also used as a lock when mounting */ + int noattach; + ulong pgrpid; + QLock debug; /* single access via devproc.c */ + RWlock ns; /* Namespace n read/one write lock */ + Mhead *mnthash[MNTHASH]; +}; + +struct Rgrp +{ + Ref ref; + Proc *rendhash[RENDHASH]; /* Rendezvous tag hash */ +}; + +struct Egrp +{ + Ref ref; + RWlock lk; + Evalue **ent; + int nent; + int ment; + ulong path; /* qid.path of next Evalue to be allocated */ + ulong vers; /* of Egrp */ +}; + +struct Evalue +{ + char *name; + char *value; + int len; + Evalue *link; + Qid qid; +}; + +struct Fgrp +{ + Ref ref; + Chan **fd; + int nfd; /* number allocated */ + int maxfd; /* highest fd in use */ + int exceed; /* debugging */ +}; + +enum +{ + DELTAFD = 20, /* incremental increase in Fgrp.fd's */ + NERR = 20 +}; + +typedef uvlong Ticks; + +enum +{ + Running, + Rendezvous, + Wakeme, +}; + +struct Proc +{ + uint state; + uint mach; + + ulong pid; + ulong parentpid; + + Pgrp *pgrp; /* Process group for namespace */ + Fgrp *fgrp; /* File descriptor group */ + Rgrp *rgrp; + + Lock rlock; /* sync sleep/wakeup with postnote */ + Rendez *r; /* rendezvous point slept on */ + Rendez sleep; /* place for syssleep/debug */ + int notepending; /* note issued but not acted on */ + int kp; /* true if a kernel process */ + + void* rendtag; /* Tag for rendezvous */ + void* rendval; /* Value for rendezvous */ + Proc *rendhash; /* Hash list for tag values */ + + int nerrlab; + Label errlab[NERR]; + char user[KNAMELEN]; + char *syserrstr; /* last error from a system call, errbuf0 or 1 */ + char *errstr; /* reason we're unwinding the error stack, errbuf1 or 0 */ + char errbuf0[ERRMAX]; + char errbuf1[ERRMAX]; + char genbuf[128]; /* buffer used e.g. for last name element from namec */ + char text[KNAMELEN]; + + Chan *slash; + Chan *dot; + + Proc *qnext; + + void (*fn)(void*); + void *arg; + + char oproc[1024]; /* reserved for os */ + +}; + +enum +{ + PRINTSIZE = 256, + MAXCRYPT = 127, + NUMSIZE = 12, /* size of formatted number */ + MB = (1024*1024), + READSTR = 1000, /* temporary buffer size for device reads */ +}; + +extern char* conffile; +extern int cpuserver; +extern Dev* devtab[]; +extern char *eve; +extern char hostdomain[]; +extern uchar initcode[]; +extern Queue* kbdq; +extern Queue* kprintoq; +extern Ref noteidalloc; +extern Palloc palloc; +extern Queue *serialoq; +extern char* statename[]; +extern int nsyscall; +extern char *sysname; +extern uint qiomaxatomic; +extern Conf conf; +enum +{ + LRESPROF = 3, +}; + +/* + * action log + */ +struct Log { + Lock lk; + int opens; + char* buf; + char *end; + char *rptr; + int len; + int nlog; + int minread; + + int logmask; /* mask of things to debug */ + + QLock readq; + Rendez readr; +}; + +struct Logflag { + char* name; + int mask; +}; + +enum +{ + NCMDFIELD = 128 +}; + +struct Cmdbuf +{ + char *buf; + char **f; + int nf; +}; + +struct Cmdtab +{ + int index; /* used by client to switch on result */ + char *cmd; /* command name */ + int narg; /* expected #args; 0 ==> variadic */ +}; + +/* queue state bits, Qmsg, Qcoalesce, and Qkick can be set in qopen */ +enum +{ + /* Queue.state */ + Qstarve = (1<<0), /* consumer starved */ + Qmsg = (1<<1), /* message stream */ + Qclosed = (1<<2), /* queue has been closed/hungup */ + Qflow = (1<<3), /* producer flow controlled */ + Qcoalesce = (1<<4), /* coallesce packets on read */ + Qkick = (1<<5), /* always call the kick routine after qwrite */ +}; + +#define DEVDOTDOT -1 + +extern Proc *_getproc(void); +extern void _setproc(Proc*); +#define up (_getproc()) diff --git a/sys/src/cmd/unix/drawterm/kern/data.c b/sys/src/cmd/unix/drawterm/kern/data.c new file mode 100755 index 000000000..735e5e0c4 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/data.c @@ -0,0 +1,31 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +Proc *up; +Conf conf = +{ + 1, + 100, + 0, + 1024*1024*1024, + 1024*1024*1024, + 1024*1024*1024, + 1024*1024*1024, + 1024*1024*1024, + 1024*1024*1024, + 1024*1024*1024, + 1024*1024*1024, + 1024*1024*1024, + 1024*1024*1024, + 1024*1024*1024, + 1024*1024*1024, + 0, +}; + +char *eve = "eve"; +ulong kerndate; +int cpuserver; +char hostdomain[] = "drawterm.net"; diff --git a/sys/src/cmd/unix/drawterm/kern/dev.c b/sys/src/cmd/unix/drawterm/kern/dev.c new file mode 100755 index 000000000..83bf624f3 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/dev.c @@ -0,0 +1,468 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +extern ulong kerndate; + +void +mkqid(Qid *q, vlong path, ulong vers, int type) +{ + q->type = type; + q->vers = vers; + q->path = path; +} + +int +devno(int c, int user) +{ + int i; + + for(i = 0; devtab[i] != nil; i++) { + if(devtab[i]->dc == c) + return i; + } + if(user == 0) + panic("devno %C 0x%ux", c, c); + + return -1; +} + +void +devdir(Chan *c, Qid qid, char *n, vlong length, char *user, long perm, Dir *db) +{ + db->name = n; + if(c->flag&CMSG) + qid.type |= QTMOUNT; + db->qid = qid; + db->type = devtab[c->type]->dc; + db->dev = c->dev; + db->mode = perm; + db->mode |= qid.type << 24; + db->atime = seconds(); + db->mtime = kerndate; + db->length = length; + db->uid = user; + db->gid = eve; + db->muid = user; +} + +/* + * (here, Devgen is the prototype; devgen is the function in dev.c.) + * + * a Devgen is expected to return the directory entry for ".." + * if you pass it s==DEVDOTDOT (-1). otherwise... + * + * there are two contradictory rules. + * + * (i) if c is a directory, a Devgen is expected to list its children + * as you iterate s. + * + * (ii) whether or not c is a directory, a Devgen is expected to list + * its siblings as you iterate s. + * + * devgen always returns the list of children in the root + * directory. thus it follows (i) when c is the root and (ii) otherwise. + * many other Devgens follow (i) when c is a directory and (ii) otherwise. + * + * devwalk assumes (i). it knows that devgen breaks (i) + * for children that are themselves directories, and explicitly catches them. + * + * devstat assumes (ii). if the Devgen in question follows (i) + * for this particular c, devstat will not find the necessary info. + * with our particular Devgen functions, this happens only for + * directories, so devstat makes something up, assuming + * c->name, c->qid, eve, DMDIR|0555. + * + * devdirread assumes (i). the callers have to make sure + * that the Devgen satisfies (i) for the chan being read. + */ +/* + * the zeroth element of the table MUST be the directory itself for .. +*/ +int +devgen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp) +{ + if(tab == 0) + return -1; + if(i == DEVDOTDOT){ + /* nothing */ + }else if(name){ + for(i=1; i<ntab; i++) + if(strcmp(tab[i].name, name) == 0) + break; + if(i==ntab) + return -1; + tab += i; + }else{ + /* skip over the first element, that for . itself */ + i++; + if(i >= ntab) + return -1; + tab += i; + } + devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp); + return 1; +} + +void +devreset(void) +{ +} + +void +devinit(void) +{ +} + +void +devshutdown(void) +{ +} + +Chan* +devattach(int tc, char *spec) +{ + Chan *c; + char *buf; + + c = newchan(); + mkqid(&c->qid, 0, 0, QTDIR); + c->type = devno(tc, 0); + if(spec == nil) + spec = ""; + buf = smalloc(4+strlen(spec)+1); + sprint(buf, "#%C%s", tc, spec); + c->name = newcname(buf); + free(buf); + return c; +} + + +Chan* +devclone(Chan *c) +{ + Chan *nc; + + if(c->flag & COPEN) + panic("clone of open file type %C\n", devtab[c->type]->dc); + + nc = newchan(); + + nc->type = c->type; + nc->dev = c->dev; + nc->mode = c->mode; + nc->qid = c->qid; + nc->offset = c->offset; + nc->umh = nil; + nc->mountid = c->mountid; + nc->aux = c->aux; + nc->pgrpid = c->pgrpid; + nc->mid = c->mid; + nc->mqid = c->mqid; + nc->mcp = c->mcp; + return nc; +} + +Walkqid* +devwalk(Chan *c, Chan *nc, char **name, int nname, Dirtab *tab, int ntab, Devgen *gen) +{ + int i, j, alloc; + Walkqid *wq; + char *n; + Dir dir; + + if(nname > 0) + isdir(c); + + alloc = 0; + wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); + if(waserror()){ + if(alloc && wq->clone!=nil) + cclose(wq->clone); + free(wq); + return nil; + } + if(nc == nil){ + nc = devclone(c); + nc->type = 0; /* device doesn't know about this channel yet */ + alloc = 1; + } + wq->clone = nc; + + for(j=0; j<nname; j++){ + if(!(nc->qid.type&QTDIR)){ + if(j==0) + error(Enotdir); + goto Done; + } + n = name[j]; + if(strcmp(n, ".") == 0){ + Accept: + wq->qid[wq->nqid++] = nc->qid; + continue; + } + if(strcmp(n, "..") == 0){ + if((*gen)(nc, nil, tab, ntab, DEVDOTDOT, &dir) != 1){ + print("devgen walk .. in dev%s %llux broken\n", + devtab[nc->type]->name, nc->qid.path); + error("broken devgen"); + } + nc->qid = dir.qid; + goto Accept; + } + /* + * Ugly problem: If we're using devgen, make sure we're + * walking the directory itself, represented by the first + * entry in the table, and not trying to step into a sub- + * directory of the table, e.g. /net/net. Devgen itself + * should take care of the problem, but it doesn't have + * the necessary information (that we're doing a walk). + */ + if(gen==devgen && nc->qid.path!=tab[0].qid.path) + goto Notfound; + for(i=0;; i++) { + switch((*gen)(nc, n, tab, ntab, i, &dir)){ + case -1: + Notfound: + if(j == 0) + error(Enonexist); + kstrcpy(up->errstr, Enonexist, ERRMAX); + goto Done; + case 0: + continue; + case 1: + if(strcmp(n, dir.name) == 0){ + nc->qid = dir.qid; + goto Accept; + } + continue; + } + } + } + /* + * We processed at least one name, so will return some data. + * If we didn't process all nname entries succesfully, we drop + * the cloned channel and return just the Qids of the walks. + */ +Done: + poperror(); + if(wq->nqid < nname){ + if(alloc) + cclose(wq->clone); + wq->clone = nil; + }else if(wq->clone){ + /* attach cloned channel to same device */ + wq->clone->type = c->type; + } + return wq; +} + +int +devstat(Chan *c, uchar *db, int n, Dirtab *tab, int ntab, Devgen *gen) +{ + int i; + Dir dir; + char *p, *elem; + + for(i=0;; i++) + switch((*gen)(c, nil, tab, ntab, i, &dir)){ + case -1: + if(c->qid.type & QTDIR){ + if(c->name == nil) + elem = "???"; + else if(strcmp(c->name->s, "/") == 0) + elem = "/"; + else + for(elem=p=c->name->s; *p; p++) + if(*p == '/') + elem = p+1; + devdir(c, c->qid, elem, 0, eve, DMDIR|0555, &dir); + n = convD2M(&dir, db, n); + if(n == 0) + error(Ebadarg); + return n; + } + print("devstat %C %llux\n", devtab[c->type]->dc, c->qid.path); + + error(Enonexist); + case 0: + break; + case 1: + if(c->qid.path == dir.qid.path) { + if(c->flag&CMSG) + dir.mode |= DMMOUNT; + n = convD2M(&dir, db, n); + if(n == 0) + error(Ebadarg); + return n; + } + break; + } + error(Egreg); /* not reached? */ + return -1; +} + +long +devdirread(Chan *c, char *d, long n, Dirtab *tab, int ntab, Devgen *gen) +{ + long m, dsz; + struct{ + Dir d; + char slop[100]; + }dir; + + for(m=0; m<n; c->dri++) { + switch((*gen)(c, nil, tab, ntab, c->dri, &dir.d)){ + case -1: + return m; + + case 0: + break; + + case 1: + dsz = convD2M(&dir.d, (uchar*)d, n-m); + if(dsz <= BIT16SZ){ /* <= not < because this isn't stat; read is stuck */ + if(m == 0) + error(Eshort); + return m; + } + m += dsz; + d += dsz; + break; + } + } + + return m; +} + +/* + * error(Eperm) if open permission not granted for up->user. + */ +void +devpermcheck(char *fileuid, ulong perm, int omode) +{ + ulong t; + static int access[] = { 0400, 0200, 0600, 0100 }; + + if(strcmp(up->user, fileuid) == 0) + perm <<= 0; + else + if(strcmp(up->user, eve) == 0) + perm <<= 3; + else + perm <<= 6; + + t = access[omode&3]; + if((t&perm) != t) + error(Eperm); +} + +Chan* +devopen(Chan *c, int omode, Dirtab *tab, int ntab, Devgen *gen) +{ + int i; + Dir dir; + + for(i=0;; i++) { + switch((*gen)(c, nil, tab, ntab, i, &dir)){ + case -1: + goto Return; + case 0: + break; + case 1: + if(c->qid.path == dir.qid.path) { + devpermcheck(dir.uid, dir.mode, omode); + goto Return; + } + break; + } + } +Return: + c->offset = 0; + if((c->qid.type&QTDIR) && omode!=OREAD) + error(Eperm); + c->mode = openmode(omode); + c->flag |= COPEN; + return c; +} + +void +devcreate(Chan *c, char *name, int mode, ulong perm) +{ + USED(c); + USED(name); + USED(mode); + USED(perm); + + error(Eperm); +} + +Block* +devbread(Chan *c, long n, ulong offset) +{ + Block *bp; + + bp = allocb(n); + if(bp == 0) + error(Enomem); + if(waserror()) { + freeb(bp); + nexterror(); + } + bp->wp += devtab[c->type]->read(c, bp->wp, n, offset); + poperror(); + return bp; +} + +long +devbwrite(Chan *c, Block *bp, ulong offset) +{ + long n; + + if(waserror()) { + freeb(bp); + nexterror(); + } + n = devtab[c->type]->write(c, bp->rp, BLEN(bp), offset); + poperror(); + freeb(bp); + + return n; +} + +void +devremove(Chan *c) +{ + USED(c); + error(Eperm); +} + +int +devwstat(Chan *c, uchar *a, int n) +{ + USED(c); + USED(a); + USED(n); + + error(Eperm); + return 0; +} + +void +devpower(int a) +{ + USED(a); + error(Eperm); +} + +int +devconfig(int a, char *b, DevConf *c) +{ + USED(a); + USED(b); + USED(c); + error(Eperm); + return 0; +} diff --git a/sys/src/cmd/unix/drawterm/kern/devaudio-none.c b/sys/src/cmd/unix/drawterm/kern/devaudio-none.c new file mode 100755 index 000000000..57a536651 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/devaudio-none.c @@ -0,0 +1,49 @@ +/* + * Linux and BSD + */ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "devaudio.h" + +/* maybe this should return -1 instead of sysfatal */ +void +audiodevopen(void) +{ + error("no audio support"); +} + +void +audiodevclose(void) +{ + error("no audio support"); +} + +int +audiodevread(void *a, int n) +{ + error("no audio support"); + return -1; +} + +int +audiodevwrite(void *a, int n) +{ + error("no audio support"); + return -1; +} + +void +audiodevsetvol(int what, int left, int right) +{ + error("no audio support"); +} + +void +audiodevgetvol(int what, int *left, int *right) +{ + error("no audio support"); +} + diff --git a/sys/src/cmd/unix/drawterm/kern/devaudio-unix.c b/sys/src/cmd/unix/drawterm/kern/devaudio-unix.c new file mode 100755 index 000000000..ad5af7458 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/devaudio-unix.c @@ -0,0 +1,183 @@ +/* + * Linux and BSD + */ +#include <sys/ioctl.h> +#ifdef __linux__ +#include <linux/soundcard.h> +#else +#include <sys/soundcard.h> +#endif +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "devaudio.h" + +enum +{ + Channels = 2, + Rate = 44100, + Bits = 16, + Bigendian = 1, +}; + +static int afd = -1; +static int cfd= -1; +static int speed; + +/* maybe this should return -1 instead of sysfatal */ +void +audiodevopen(void) +{ + int t; + ulong ul; + + afd = -1; + cfd = -1; + if((afd = open("/dev/dsp", OWRITE)) < 0) + goto err; + if((cfd = open("/dev/mixer", ORDWR)) < 0) + goto err; + + t = Bits; + if(ioctl(afd, SNDCTL_DSP_SAMPLESIZE, &t) < 0) + goto err; + + t = Channels-1; + if(ioctl(afd, SNDCTL_DSP_STEREO, &t) < 0) + goto err; + + speed = Rate; + ul = Rate; + if(ioctl(afd, SNDCTL_DSP_SPEED, &ul) < 0) + goto err; + + return; + +err: + if(afd >= 0) + close(afd); + afd = -1; + oserror(); +} + +void +audiodevclose(void) +{ + close(afd); + close(cfd); + afd = -1; + cfd = -1; +} + +static struct { + int id9; + int id; +} names[] = { + Vaudio, SOUND_MIXER_VOLUME, + Vbass, SOUND_MIXER_BASS, + Vtreb, SOUND_MIXER_TREBLE, + Vline, SOUND_MIXER_LINE, + Vpcm, SOUND_MIXER_PCM, + Vsynth, SOUND_MIXER_SYNTH, + Vcd, SOUND_MIXER_CD, + Vmic, SOUND_MIXER_MIC, +// "record", SOUND_MIXER_RECLEV, +// "mix", SOUND_MIXER_IMIX, +// "pcm2", SOUND_MIXER_ALTPCM, + Vspeaker, SOUND_MIXER_SPEAKER +// "line1", SOUND_MIXER_LINE1, +// "line2", SOUND_MIXER_LINE2, +// "line3", SOUND_MIXER_LINE3, +// "digital1", SOUND_MIXER_DIGITAL1, +// "digital2", SOUND_MIXER_DIGITAL2, +// "digital3", SOUND_MIXER_DIGITAL3, +// "phonein", SOUND_MIXER_PHONEIN, +// "phoneout", SOUND_MIXER_PHONEOUT, +// "radio", SOUND_MIXER_RADIO, +// "video", SOUND_MIXER_VIDEO, +// "monitor", SOUND_MIXER_MONITOR, +// "igain", SOUND_MIXER_IGAIN, +// "ogain", SOUND_MIXER_OGAIN, +}; + +static int +lookname(int id9) +{ + int i; + + for(i=0; i<nelem(names); i++) + if(names[i].id9 == id9) + return names[i].id; + return -1; +} + +void +audiodevsetvol(int what, int left, int right) +{ + int id; + ulong x; + int can, v; + + if(cfd < 0) + error("audio device not open"); + if(what == Vspeed){ + x = left; + if(ioctl(afd, SNDCTL_DSP_SPEED, &x) < 0) + oserror(); + speed = x; + return; + } + if((id = lookname(what)) < 0) + return; + if(ioctl(cfd, SOUND_MIXER_READ_DEVMASK, &can) < 0) + can = ~0; + if(!(can & (1<<id))) + return; + v = left | (right<<8); + if(ioctl(cfd, MIXER_WRITE(id), &v) < 0) + oserror(); +} + +void +audiodevgetvol(int what, int *left, int *right) +{ + int id; + int can, v; + + if(cfd < 0) + error("audio device not open"); + if(what == Vspeed){ + *left = *right = speed; + return; + } + if((id = lookname(what)) < 0) + return; + if(ioctl(cfd, SOUND_MIXER_READ_DEVMASK, &can) < 0) + can = ~0; + if(!(can & (1<<id))) + return; + if(ioctl(cfd, MIXER_READ(id), &v) < 0) + oserror(); + *left = v&0xFF; + *right = (v>>8)&0xFF; +} + +int +audiodevwrite(void *v, int n) +{ + int m, tot; + + for(tot=0; tot<n; tot+=m) + if((m = write(afd, (uchar*)v+tot, n-tot)) <= 0) + oserror(); + return tot; +} + +int +audiodevread(void *v, int n) +{ + error("no reading"); + return -1; +} diff --git a/sys/src/cmd/unix/drawterm/kern/devaudio.c b/sys/src/cmd/unix/drawterm/kern/devaudio.c new file mode 100755 index 000000000..b6d0cc512 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/devaudio.c @@ -0,0 +1,372 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "devaudio.h" + +enum +{ + Qdir = 0, + Qaudio, + Qvolume, + + Aclosed = 0, + Aread, + Awrite, + + Speed = 44100, + Ncmd = 50, /* max volume command words */ +}; + +Dirtab +audiodir[] = +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "audio", {Qaudio}, 0, 0666, + "volume", {Qvolume}, 0, 0666, +}; + +static struct +{ + QLock lk; + Rendez vous; + int amode; /* Aclosed/Aread/Awrite for /audio */ +} audio; + +#define aqlock(a) qlock(&(a)->lk) +#define aqunlock(a) qunlock(&(a)->lk) + +static struct +{ + char* name; + int flag; + int ilval; /* initial values */ + int irval; +} volumes[] = +{ + "audio", Fout, 50, 50, + "synth", Fin|Fout, 0, 0, + "cd", Fin|Fout, 0, 0, + "line", Fin|Fout, 0, 0, + "mic", Fin|Fout|Fmono, 0, 0, + "speaker", Fout|Fmono, 0, 0, + + "treb", Fout, 50, 50, + "bass", Fout, 50, 50, + + "speed", Fin|Fout|Fmono, Speed, Speed, + 0 +}; + +static char Emode[] = "illegal open mode"; +static char Evolume[] = "illegal volume specifier"; + +static void +resetlevel(void) +{ + int i; + + for(i=0; volumes[i].name; i++) + audiodevsetvol(i, volumes[i].ilval, volumes[i].irval); +} + +static void +audioinit(void) +{ +} + +static Chan* +audioattach(char *param) +{ + return devattach('A', param); +} + +static Walkqid* +audiowalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen); +} + +static int +audiostat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, audiodir, nelem(audiodir), devgen); +} + +static Chan* +audioopen(Chan *c, int omode) +{ + int amode; + + switch((ulong)c->qid.path) { + default: + error(Eperm); + break; + + case Qvolume: + case Qdir: + break; + + case Qaudio: + amode = Awrite; + if((omode&7) == OREAD) + amode = Aread; + aqlock(&audio); + if(waserror()){ + aqunlock(&audio); + nexterror(); + } + if(audio.amode != Aclosed) + error(Einuse); + audiodevopen(); + audio.amode = amode; + poperror(); + aqunlock(&audio); + break; + } + c = devopen(c, omode, audiodir, nelem(audiodir), devgen); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + + return c; +} + +static void +audioclose(Chan *c) +{ + switch((ulong)c->qid.path) { + default: + error(Eperm); + break; + + case Qdir: + case Qvolume: + break; + + case Qaudio: + if(c->flag & COPEN) { + aqlock(&audio); + audiodevclose(); + audio.amode = Aclosed; + aqunlock(&audio); + } + break; + } +} + +static long +audioread(Chan *c, void *v, long n, vlong off) +{ + int liv, riv, lov, rov; + long m; + char buf[300]; + int j; + ulong offset = off; + char *a; + + a = v; + switch((ulong)c->qid.path) { + default: + error(Eperm); + break; + + case Qdir: + return devdirread(c, a, n, audiodir, nelem(audiodir), devgen); + + case Qaudio: + if(audio.amode != Aread) + error(Emode); + aqlock(&audio); + if(waserror()){ + aqunlock(&audio); + nexterror(); + } + n = audiodevread(v, n); + poperror(); + aqunlock(&audio); + break; + + case Qvolume: + j = 0; + buf[0] = 0; + for(m=0; volumes[m].name; m++){ + audiodevgetvol(m, &lov, &rov); + liv = lov; + riv = rov; + j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name); + if((volumes[m].flag & Fmono) || (liv==riv && lov==rov)){ + if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov) + j += snprint(buf+j, sizeof(buf)-j, " %d", liv); + else{ + if(volumes[m].flag & Fin) + j += snprint(buf+j, sizeof(buf)-j, + " in %d", liv); + if(volumes[m].flag & Fout) + j += snprint(buf+j, sizeof(buf)-j, + " out %d", lov); + } + }else{ + if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && + liv==lov && riv==rov) + j += snprint(buf+j, sizeof(buf)-j, + " left %d right %d", + liv, riv); + else{ + if(volumes[m].flag & Fin) + j += snprint(buf+j, sizeof(buf)-j, + " in left %d right %d", + liv, riv); + if(volumes[m].flag & Fout) + j += snprint(buf+j, sizeof(buf)-j, + " out left %d right %d", + lov, rov); + } + } + j += snprint(buf+j, sizeof(buf)-j, "\n"); + } + return readstr(offset, a, n, buf); + } + return n; +} + +static long +audiowrite(Chan *c, void *vp, long n, vlong off) +{ + long m; + int i, v, left, right, in, out; + Cmdbuf *cb; + char *a; + + USED(off); + a = vp; + switch((ulong)c->qid.path) { + default: + error(Eperm); + break; + + case Qvolume: + v = Vaudio; + left = 1; + right = 1; + in = 1; + out = 1; + cb = parsecmd(vp, n); + if(waserror()){ + free(cb); + nexterror(); + } + + for(i = 0; i < cb->nf; i++){ + /* + * a number is volume + */ + if(cb->f[i][0] >= '0' && cb->f[i][0] <= '9') { + m = strtoul(cb->f[i], 0, 10); + if(!out) + goto cont0; + if(left && right) + audiodevsetvol(v, m, m); + else if(left) + audiodevsetvol(v, m, -1); + else if(right) + audiodevsetvol(v, -1, m); + goto cont0; + } + + for(m=0; volumes[m].name; m++) { + if(strcmp(cb->f[i], volumes[m].name) == 0) { + v = m; + in = 1; + out = 1; + left = 1; + right = 1; + goto cont0; + } + } + + if(strcmp(cb->f[i], "reset") == 0) { + resetlevel(); + goto cont0; + } + if(strcmp(cb->f[i], "in") == 0) { + in = 1; + out = 0; + goto cont0; + } + if(strcmp(cb->f[i], "out") == 0) { + in = 0; + out = 1; + goto cont0; + } + if(strcmp(cb->f[i], "left") == 0) { + left = 1; + right = 0; + goto cont0; + } + if(strcmp(cb->f[i], "right") == 0) { + left = 0; + right = 1; + goto cont0; + } + error(Evolume); + break; + cont0:; + } + free(cb); + poperror(); + break; + + case Qaudio: + if(audio.amode != Awrite) + error(Emode); + aqlock(&audio); + if(waserror()){ + aqunlock(&audio); + nexterror(); + } + n = audiodevwrite(vp, n); + poperror(); + aqunlock(&audio); + break; + } + return n; +} + +void +audioswab(uchar *a, uint n) +{ + ulong *p, *ep, b; + + p = (ulong*)a; + ep = p + (n>>2); + while(p < ep) { + b = *p; + b = (b>>24) | (b<<24) | + ((b&0xff0000) >> 8) | + ((b&0x00ff00) << 8); + *p++ = b; + } +} + +Dev audiodevtab = { + 'A', + "audio", + + devreset, + audioinit, + devshutdown, + audioattach, + audiowalk, + audiostat, + audioopen, + devcreate, + audioclose, + audioread, + devbread, + audiowrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/sys/src/cmd/unix/drawterm/kern/devaudio.h b/sys/src/cmd/unix/drawterm/kern/devaudio.h new file mode 100755 index 000000000..e6cc7ed21 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/devaudio.h @@ -0,0 +1,25 @@ +enum +{ + Fmono = 1, + Fin = 2, + Fout = 4, + + Vaudio = 0, + Vsynth, + Vcd, + Vline, + Vmic, + Vspeaker, + Vtreb, + Vbass, + Vspeed, + Vpcm, + Nvol, +}; + +void audiodevopen(void); +void audiodevclose(void); +int audiodevread(void*, int); +int audiodevwrite(void*, int); +void audiodevgetvol(int, int*, int*); +void audiodevsetvol(int, int, int); diff --git a/sys/src/cmd/unix/drawterm/kern/devcons.c b/sys/src/cmd/unix/drawterm/kern/devcons.c new file mode 100755 index 000000000..bd80d6eae --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/devcons.c @@ -0,0 +1,1193 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include "keyboard.h" + +void (*consdebug)(void) = 0; +void (*screenputs)(char*, int) = 0; + +Queue* kbdq; /* unprocessed console input */ +Queue* lineq; /* processed console input */ +Queue* serialoq; /* serial console output */ +Queue* kprintoq; /* console output, for /dev/kprint */ +long kprintinuse; /* test and set whether /dev/kprint is open */ +Lock kprintlock; +int iprintscreenputs = 0; + +int panicking; + +struct +{ + int exiting; + int machs; +} active; + +static struct +{ + QLock lk; + + int raw; /* true if we shouldn't process input */ + int ctl; /* number of opens to the control file */ + int x; /* index into line */ + char line[1024]; /* current input line */ + + int count; + int ctlpoff; + + /* a place to save up characters at interrupt time before dumping them in the queue */ + Lock lockputc; + char istage[1024]; + char *iw; + char *ir; + char *ie; +} kbd = { + { 0 }, + 0, + 0, + 0, + { 0 }, + 0, + 0, + { 0 }, + { 0 }, + kbd.istage, + kbd.istage, + kbd.istage + sizeof(kbd.istage), +}; + +char *sysname; +vlong fasthz; + +static int readtime(ulong, char*, int); +static int readbintime(char*, int); +static int writetime(char*, int); +static int writebintime(char*, int); + +enum +{ + CMreboot, + CMpanic, +}; + +Cmdtab rebootmsg[] = +{ + CMreboot, "reboot", 0, + CMpanic, "panic", 0, +}; + +int +return0(void *v) +{ + return 0; +} + +void +printinit(void) +{ + lineq = qopen(2*1024, 0, 0, nil); + if(lineq == nil) + panic("printinit"); + qnoblock(lineq, 1); + + kbdq = qopen(4*1024, 0, 0, 0); + if(kbdq == nil) + panic("kbdinit"); + qnoblock(kbdq, 1); +} + +int +consactive(void) +{ + if(serialoq) + return qlen(serialoq) > 0; + return 0; +} + +void +prflush(void) +{ +/* + ulong now; + + now = m->ticks; + while(consactive()) + if(m->ticks - now >= HZ) + break; +*/ +} + +/* + * Print a string on the console. Convert \n to \r\n for serial + * line consoles. Locking of the queues is left up to the screen + * or uart code. Multi-line messages to serial consoles may get + * interspersed with other messages. + */ +static void +putstrn0(char *str, int n, int usewrite) +{ + /* + * if someone is reading /dev/kprint, + * put the message there. + * if not and there's an attached bit mapped display, + * put the message there. + * + * if there's a serial line being used as a console, + * put the message there. + */ + if(kprintoq != nil && !qisclosed(kprintoq)){ + if(usewrite) + qwrite(kprintoq, str, n); + else + qiwrite(kprintoq, str, n); + }else if(screenputs != 0) + screenputs(str, n); +} + +void +putstrn(char *str, int n) +{ + putstrn0(str, n, 0); +} + +int noprint; + +int +print(char *fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + if(noprint) + return -1; + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + putstrn(buf, n); + + return n; +} + +void +panic(char *fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + kprintoq = nil; /* don't try to write to /dev/kprint */ + + if(panicking) + for(;;); + panicking = 1; + + splhi(); + strcpy(buf, "panic: "); + va_start(arg, fmt); + n = vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + buf[n] = '\n'; + uartputs(buf, n+1); + if(consdebug) + (*consdebug)(); + spllo(); + prflush(); + putstrn(buf, n+1); + dumpstack(); + + exit(1); +} + +int +pprint(char *fmt, ...) +{ + int n; + Chan *c; + va_list arg; + char buf[2*PRINTSIZE]; + + if(up == nil || up->fgrp == nil) + return 0; + + c = up->fgrp->fd[2]; + if(c==0 || (c->mode!=OWRITE && c->mode!=ORDWR)) + return 0; + n = sprint(buf, "%s %lud: ", up->text, up->pid); + va_start(arg, fmt); + n = vseprint(buf+n, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + + if(waserror()) + return 0; + devtab[c->type]->write(c, buf, n, c->offset); + poperror(); + + lock(&c->ref.lk); + c->offset += n; + unlock(&c->ref.lk); + + return n; +} + +static void +echoscreen(char *buf, int n) +{ + char *e, *p; + char ebuf[128]; + int x; + + p = ebuf; + e = ebuf + sizeof(ebuf) - 4; + while(n-- > 0){ + if(p >= e){ + screenputs(ebuf, p - ebuf); + p = ebuf; + } + x = *buf++; + if(x == 0x15){ + *p++ = '^'; + *p++ = 'U'; + *p++ = '\n'; + } else + *p++ = x; + } + if(p != ebuf) + screenputs(ebuf, p - ebuf); +} + +static void +echoserialoq(char *buf, int n) +{ + char *e, *p; + char ebuf[128]; + int x; + + p = ebuf; + e = ebuf + sizeof(ebuf) - 4; + while(n-- > 0){ + if(p >= e){ + qiwrite(serialoq, ebuf, p - ebuf); + p = ebuf; + } + x = *buf++; + if(x == '\n'){ + *p++ = '\r'; + *p++ = '\n'; + } else if(x == 0x15){ + *p++ = '^'; + *p++ = 'U'; + *p++ = '\n'; + } else + *p++ = x; + } + if(p != ebuf) + qiwrite(serialoq, ebuf, p - ebuf); +} + +static void +echo(char *buf, int n) +{ + static int ctrlt; + int x; + char *e, *p; + + e = buf+n; + for(p = buf; p < e; p++){ + switch(*p){ + case 0x10: /* ^P */ + if(cpuserver && !kbd.ctlpoff){ + active.exiting = 1; + return; + } + break; + case 0x14: /* ^T */ + ctrlt++; + if(ctrlt > 2) + ctrlt = 2; + continue; + } + + if(ctrlt != 2) + continue; + + /* ^T escapes */ + ctrlt = 0; + switch(*p){ + case 'S': + x = splhi(); + dumpstack(); + procdump(); + splx(x); + return; + case 's': + dumpstack(); + return; + case 'x': + xsummary(); + ixsummary(); + mallocsummary(); + pagersummary(); + return; + case 'd': + if(consdebug == 0) + consdebug = rdb; + else + consdebug = 0; + print("consdebug now 0x%p\n", consdebug); + return; + case 'D': + if(consdebug == 0) + consdebug = rdb; + consdebug(); + return; + case 'p': + x = spllo(); + procdump(); + splx(x); + return; + case 'q': + scheddump(); + return; + case 'k': + killbig(); + return; + case 'r': + exit(0); + return; + } + } + + qproduce(kbdq, buf, n); + if(kbd.raw) + return; + if(screenputs != 0) + echoscreen(buf, n); + if(serialoq) + echoserialoq(buf, n); +} + +/* + * Called by a uart interrupt for console input. + * + * turn '\r' into '\n' before putting it into the queue. + */ +int +kbdcr2nl(Queue *q, int ch) +{ + char *next; + + USED(q); + ilock(&kbd.lockputc); /* just a mutex */ + if(ch == '\r' && !kbd.raw) + ch = '\n'; + next = kbd.iw+1; + if(next >= kbd.ie) + next = kbd.istage; + if(next != kbd.ir){ + *kbd.iw = ch; + kbd.iw = next; + } + iunlock(&kbd.lockputc); + return 0; +} +static +void +_kbdputc(int c) +{ + Rune r; + char buf[UTFmax]; + int n; + + r = c; + n = runetochar(buf, &r); + if(n == 0) + return; + echo(buf, n); +// kbd.c = r; +// qproduce(kbdq, buf, n); +} + +/* _kbdputc, but with compose translation */ +int +kbdputc(Queue *q, int c) +{ + int i; + static int collecting, nk; + static Rune kc[5]; + + if(c == Kalt){ + collecting = 1; + nk = 0; + return 0; + } + + if(!collecting){ + _kbdputc(c); + return 0; + } + + kc[nk++] = c; + c = latin1(kc, nk); + if(c < -1) /* need more keystrokes */ + return 0; + if(c != -1) /* valid sequence */ + _kbdputc(c); + else + for(i=0; i<nk; i++) + _kbdputc(kc[i]); + nk = 0; + collecting = 0; + + return 0; +} + + +enum{ + Qdir, + Qbintime, + Qcons, + Qconsctl, + Qcpunote, + Qcputime, + Qdrivers, + Qkprint, + Qhostdomain, + Qhostowner, + Qnull, + Qosversion, + Qpgrpid, + Qpid, + Qppid, + Qrandom, + Qreboot, + Qsecstore, + Qshowfile, + Qsnarf, + Qswap, + Qsysname, + Qsysstat, + Qtime, + Quser, + Qzero, +}; + +enum +{ + VLNUMSIZE= 22, +}; + +static Dirtab consdir[]={ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "bintime", {Qbintime}, 24, 0664, + "cons", {Qcons}, 0, 0660, + "consctl", {Qconsctl}, 0, 0220, + "cpunote", {Qcpunote}, 0, 0444, + "cputime", {Qcputime}, 6*NUMSIZE, 0444, + "drivers", {Qdrivers}, 0, 0444, + "hostdomain", {Qhostdomain}, DOMLEN, 0664, + "hostowner", {Qhostowner}, 0, 0664, + "kprint", {Qkprint, 0, QTEXCL}, 0, DMEXCL|0440, + "null", {Qnull}, 0, 0666, + "osversion", {Qosversion}, 0, 0444, + "pgrpid", {Qpgrpid}, NUMSIZE, 0444, + "pid", {Qpid}, NUMSIZE, 0444, + "ppid", {Qppid}, NUMSIZE, 0444, + "random", {Qrandom}, 0, 0444, + "reboot", {Qreboot}, 0, 0664, + "secstore", {Qsecstore}, 0, 0666, + "showfile", {Qshowfile}, 0, 0220, + "snarf", {Qsnarf}, 0, 0666, + "swap", {Qswap}, 0, 0664, + "sysname", {Qsysname}, 0, 0664, + "sysstat", {Qsysstat}, 0, 0666, + "time", {Qtime}, NUMSIZE+3*VLNUMSIZE, 0664, + "user", {Quser}, 0, 0666, + "zero", {Qzero}, 0, 0444, +}; + +char secstorebuf[65536]; +Dirtab *secstoretab = &consdir[Qsecstore]; +Dirtab *snarftab = &consdir[Qsnarf]; + +int +readnum(ulong off, char *buf, ulong n, ulong val, int size) +{ + char tmp[64]; + + snprint(tmp, sizeof(tmp), "%*.0lud", size-1, val); + tmp[size-1] = ' '; + if(off >= size) + return 0; + if(off+n > size) + n = size-off; + memmove(buf, tmp+off, n); + return n; +} + +int +readstr(ulong off, char *buf, ulong n, char *str) +{ + int size; + + size = strlen(str); + if(off >= size) + return 0; + if(off+n > size) + n = size-off; + memmove(buf, str+off, n); + return n; +} + +static void +consinit(void) +{ + todinit(); + randominit(); + /* + * at 115200 baud, the 1024 char buffer takes 56 ms to process, + * processing it every 22 ms should be fine + */ +/* addclock0link(kbdputcclock, 22); */ +} + +static Chan* +consattach(char *spec) +{ + return devattach('c', spec); +} + +static Walkqid* +conswalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name,nname, consdir, nelem(consdir), devgen); +} + +static int +consstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, consdir, nelem(consdir), devgen); +} + +static Chan* +consopen(Chan *c, int omode) +{ + c->aux = nil; + c = devopen(c, omode, consdir, nelem(consdir), devgen); + switch((ulong)c->qid.path){ + case Qconsctl: + qlock(&kbd.lk); + kbd.ctl++; + qunlock(&kbd.lk); + break; + + case Qkprint: + lock(&kprintlock); + if(kprintinuse != 0){ + c->flag &= ~COPEN; + unlock(&kprintlock); + error(Einuse); + } + kprintinuse = 1; + unlock(&kprintlock); + if(kprintoq == nil){ + kprintoq = qopen(8*1024, Qcoalesce, 0, 0); + if(kprintoq == nil){ + c->flag &= ~COPEN; + error(Enomem); + } + qnoblock(kprintoq, 1); + }else + qreopen(kprintoq); + c->iounit = qiomaxatomic; + break; + + case Qsecstore: + if(omode == ORDWR) + error(Eperm); + if(omode != OREAD) + memset(secstorebuf, 0, sizeof secstorebuf); + break; + + case Qsnarf: + if(omode == ORDWR) + error(Eperm); + if(omode == OREAD) + c->aux = strdup(""); + else + c->aux = mallocz(SnarfSize, 1); + break; + } + return c; +} + +static void +consclose(Chan *c) +{ + switch((ulong)c->qid.path){ + /* last close of control file turns off raw */ + case Qconsctl: + if(c->flag&COPEN){ + qlock(&kbd.lk); + if(--kbd.ctl == 0) + kbd.raw = 0; + qunlock(&kbd.lk); + } + break; + + /* close of kprint allows other opens */ + case Qkprint: + if(c->flag & COPEN){ + kprintinuse = 0; + qhangup(kprintoq, nil); + } + break; + + case Qsnarf: + if(c->mode == OWRITE) + clipwrite(c->aux); + free(c->aux); + break; + } +} + +static long +consread(Chan *c, void *buf, long n, vlong off) +{ + char *b; + char tmp[128]; /* must be >= 6*NUMSIZE */ + char *cbuf = buf; + int ch, i, eol; + vlong offset = off; + + if(n <= 0) + return n; + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, buf, n, consdir, nelem(consdir), devgen); + + case Qcons: + qlock(&kbd.lk); + if(waserror()) { + qunlock(&kbd.lk); + nexterror(); + } + if(kbd.raw) { + if(qcanread(lineq)) + n = qread(lineq, buf, n); + else { + /* read as much as possible */ + do { + i = qread(kbdq, cbuf, n); + cbuf += i; + n -= i; + } while (n>0 && qcanread(kbdq)); + n = cbuf - (char*)buf; + } + } else { + while(!qcanread(lineq)) { + qread(kbdq, &kbd.line[kbd.x], 1); + ch = kbd.line[kbd.x]; + eol = 0; + switch(ch){ + case '\b': + if(kbd.x) + kbd.x--; + break; + case 0x15: + kbd.x = 0; + break; + case '\n': + case 0x04: + eol = 1; + default: + kbd.line[kbd.x++] = ch; + break; + } + if(kbd.x == sizeof(kbd.line) || eol){ + if(ch == 0x04) + kbd.x--; + qwrite(lineq, kbd.line, kbd.x); + kbd.x = 0; + } + } + n = qread(lineq, buf, n); + } + qunlock(&kbd.lk); + poperror(); + return n; + + case Qcpunote: + sleep(&up->sleep, return0, nil); + + case Qcputime: + return 0; + + case Qkprint: + return qread(kprintoq, buf, n); + + case Qpgrpid: + return readnum((ulong)offset, buf, n, up->pgrp->pgrpid, NUMSIZE); + + case Qpid: + return readnum((ulong)offset, buf, n, up->pid, NUMSIZE); + + case Qppid: + return readnum((ulong)offset, buf, n, up->parentpid, NUMSIZE); + + case Qtime: + return readtime((ulong)offset, buf, n); + + case Qbintime: + return readbintime(buf, n); + + case Qhostowner: + return readstr((ulong)offset, buf, n, eve); + + case Qhostdomain: + return readstr((ulong)offset, buf, n, hostdomain); + + case Quser: + return readstr((ulong)offset, buf, n, up->user); + + case Qnull: + return 0; + + case Qsnarf: + if(offset == 0){ + free(c->aux); + c->aux = clipread(); + } + if(c->aux == nil) + return 0; + return readstr(offset, buf, n, c->aux); + + case Qsecstore: + return readstr(offset, buf, n, secstorebuf); + + case Qsysstat: + return 0; + + case Qswap: + return 0; + + case Qsysname: + if(sysname == nil) + return 0; + return readstr((ulong)offset, buf, n, sysname); + + case Qrandom: + return randomread(buf, n); + + case Qdrivers: + b = malloc(READSTR); + if(b == nil) + error(Enomem); + n = 0; + for(i = 0; devtab[i] != nil; i++) + n += snprint(b+n, READSTR-n, "#%C %s\n", devtab[i]->dc, devtab[i]->name); + if(waserror()){ + free(b); + nexterror(); + } + n = readstr((ulong)offset, buf, n, b); + free(b); + poperror(); + return n; + + case Qzero: + memset(buf, 0, n); + return n; + + case Qosversion: + snprint(tmp, sizeof tmp, "2000"); + n = readstr((ulong)offset, buf, n, tmp); + return n; + + default: + print("consread 0x%llux\n", c->qid.path); + error(Egreg); + } + return -1; /* never reached */ +} + +static long +conswrite(Chan *c, void *va, long n, vlong off) +{ + char buf[256]; + long l, bp; + char *a = va; + int fd; + Chan *swc; + ulong offset = off; + Cmdbuf *cb; + Cmdtab *ct; + + switch((ulong)c->qid.path){ + case Qcons: + /* + * Can't page fault in putstrn, so copy the data locally. + */ + l = n; + while(l > 0){ + bp = l; + if(bp > sizeof buf) + bp = sizeof buf; + memmove(buf, a, bp); + putstrn0(buf, bp, 1); + a += bp; + l -= bp; + } + break; + + case Qconsctl: + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + strncpy(buf, a, n); + buf[n] = 0; + for(a = buf; a;){ + if(strncmp(a, "rawon", 5) == 0){ + qlock(&kbd.lk); + if(kbd.x){ + qwrite(kbdq, kbd.line, kbd.x); + kbd.x = 0; + } + kbd.raw = 1; + qunlock(&kbd.lk); + } else if(strncmp(a, "rawoff", 6) == 0){ + qlock(&kbd.lk); + kbd.raw = 0; + kbd.x = 0; + qunlock(&kbd.lk); + } else if(strncmp(a, "ctlpon", 6) == 0){ + kbd.ctlpoff = 0; + } else if(strncmp(a, "ctlpoff", 7) == 0){ + kbd.ctlpoff = 1; + } + if((a = strchr(a, ' '))) + a++; + } + break; + + case Qtime: + if(!iseve()) + error(Eperm); + return writetime(a, n); + + case Qbintime: + if(!iseve()) + error(Eperm); + return writebintime(a, n); + + case Qhostowner: + return hostownerwrite(a, n); + + case Qhostdomain: + return hostdomainwrite(a, n); + + case Quser: + return userwrite(a, n); + + case Qnull: + break; + + case Qreboot: + if(!iseve()) + error(Eperm); + cb = parsecmd(a, n); + + if(waserror()) { + free(cb); + nexterror(); + } + ct = lookupcmd(cb, rebootmsg, nelem(rebootmsg)); + switch(ct->index) { + case CMreboot: + rebootcmd(cb->nf-1, cb->f+1); + break; + case CMpanic: + panic("/dev/reboot"); + } + poperror(); + free(cb); + break; + + case Qsecstore: + if(offset >= sizeof secstorebuf || offset+n+1 >= sizeof secstorebuf) + error(Etoobig); + secstoretab->qid.vers++; + memmove(secstorebuf+offset, va, n); + return n; + + case Qshowfile: + return showfilewrite(a, n); + + case Qsnarf: + if(offset >= SnarfSize || offset+n >= SnarfSize) + error(Etoobig); + snarftab->qid.vers++; + memmove((uchar*)c->aux+offset, va, n); + return n; + + case Qsysstat: + n = 0; + break; + + case Qswap: + if(n >= sizeof buf) + error(Egreg); + memmove(buf, va, n); /* so we can NUL-terminate */ + buf[n] = 0; + /* start a pager if not already started */ + if(strncmp(buf, "start", 5) == 0){ + kickpager(); + break; + } + if(cpuserver && !iseve()) + error(Eperm); + if(buf[0]<'0' || '9'<buf[0]) + error(Ebadarg); + fd = strtoul(buf, 0, 0); + swc = fdtochan(fd, -1, 1, 1); + setswapchan(swc); + break; + + case Qsysname: + if(offset != 0) + error(Ebadarg); + if(n <= 0 || n >= sizeof buf) + error(Ebadarg); + strncpy(buf, a, n); + buf[n] = 0; + if(buf[n-1] == '\n') + buf[n-1] = 0; + kstrdup(&sysname, buf); + break; + + default: + print("conswrite: 0x%llux\n", c->qid.path); + error(Egreg); + } + return n; +} + +Dev consdevtab = { + 'c', + "cons", + + devreset, + consinit, + devshutdown, + consattach, + conswalk, + consstat, + consopen, + devcreate, + consclose, + consread, + devbread, + conswrite, + devbwrite, + devremove, + devwstat, +}; + +static uvlong uvorder = (uvlong) 0x0001020304050607ULL; + +static uchar* +le2vlong(vlong *to, uchar *f) +{ + uchar *t, *o; + int i; + + t = (uchar*)to; + o = (uchar*)&uvorder; + for(i = 0; i < sizeof(vlong); i++) + t[o[i]] = f[i]; + return f+sizeof(vlong); +} + +static uchar* +vlong2le(uchar *t, vlong from) +{ + uchar *f, *o; + int i; + + f = (uchar*)&from; + o = (uchar*)&uvorder; + for(i = 0; i < sizeof(vlong); i++) + t[i] = f[o[i]]; + return t+sizeof(vlong); +} + +static long order = 0x00010203; + +static uchar* +le2long(long *to, uchar *f) +{ + uchar *t, *o; + int i; + + t = (uchar*)to; + o = (uchar*)ℴ + for(i = 0; i < sizeof(long); i++) + t[o[i]] = f[i]; + return f+sizeof(long); +} + +/* +static uchar* +long2le(uchar *t, long from) +{ + uchar *f, *o; + int i; + + f = (uchar*)&from; + o = (uchar*)ℴ + for(i = 0; i < sizeof(long); i++) + t[i] = f[o[i]]; + return t+sizeof(long); +} +*/ + +char *Ebadtimectl = "bad time control"; + +/* + * like the old #c/time but with added info. Return + * + * secs nanosecs fastticks fasthz + */ +static int +readtime(ulong off, char *buf, int n) +{ + vlong nsec, ticks; + long sec; + char str[7*NUMSIZE]; + + nsec = todget(&ticks); + if(fasthz == (vlong)0) + fastticks((uvlong*)&fasthz); + sec = nsec/((uvlong) 1000000000); + snprint(str, sizeof(str), "%*.0lud %*.0llud %*.0llud %*.0llud ", + NUMSIZE-1, sec, + VLNUMSIZE-1, nsec, + VLNUMSIZE-1, ticks, + VLNUMSIZE-1, fasthz); + return readstr(off, buf, n, str); +} + +/* + * set the time in seconds + */ +static int +writetime(char *buf, int n) +{ + char b[13]; + long i; + vlong now; + + if(n >= sizeof(b)) + error(Ebadtimectl); + strncpy(b, buf, n); + b[n] = 0; + i = strtol(b, 0, 0); + if(i <= 0) + error(Ebadtimectl); + now = i*((vlong) 1000000000); + todset(now, 0, 0); + return n; +} + +/* + * read binary time info. all numbers are little endian. + * ticks and nsec are syncronized. + */ +static int +readbintime(char *buf, int n) +{ + int i; + vlong nsec, ticks; + uchar *b = (uchar*)buf; + + i = 0; + if(fasthz == (vlong)0) + fastticks((uvlong*)&fasthz); + nsec = todget(&ticks); + if(n >= 3*sizeof(uvlong)){ + vlong2le(b+2*sizeof(uvlong), fasthz); + i += sizeof(uvlong); + } + if(n >= 2*sizeof(uvlong)){ + vlong2le(b+sizeof(uvlong), ticks); + i += sizeof(uvlong); + } + if(n >= 8){ + vlong2le(b, nsec); + i += sizeof(vlong); + } + return i; +} + +/* + * set any of the following + * - time in nsec + * - nsec trim applied over some seconds + * - clock frequency + */ +static int +writebintime(char *buf, int n) +{ + uchar *p; + vlong delta; + long period; + + n--; + p = (uchar*)buf + 1; + switch(*buf){ + case 'n': + if(n < sizeof(vlong)) + error(Ebadtimectl); + le2vlong(&delta, p); + todset(delta, 0, 0); + break; + case 'd': + if(n < sizeof(vlong)+sizeof(long)) + error(Ebadtimectl); + p = le2vlong(&delta, p); + le2long(&period, p); + todset(-1, delta, period); + break; + case 'f': + if(n < sizeof(uvlong)) + error(Ebadtimectl); + le2vlong(&fasthz, p); + todsetfreq(fasthz); + break; + } + return n; +} + + +int +iprint(char *fmt, ...) +{ + int n, s; + va_list arg; + char buf[PRINTSIZE]; + + s = splhi(); + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + if(screenputs != 0 && iprintscreenputs) + screenputs(buf, n); +#undef write + write(2, buf, n); + splx(s); + + return n; +} + diff --git a/sys/src/cmd/unix/drawterm/kern/devdraw.c b/sys/src/cmd/unix/drawterm/kern/devdraw.c new file mode 100755 index 000000000..8b6148e3c --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/devdraw.c @@ -0,0 +1,2148 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <memlayer.h> +#include <cursor.h> +#include "screen.h" + +enum +{ + Qtopdir = 0, + Qnew, + Q3rd, + Q2nd, + Qcolormap, + Qctl, + Qdata, + Qrefresh, +}; + +/* + * Qid path is: + * 4 bits of file type (qids above) + * 24 bits of mux slot number +1; 0 means not attached to client + */ +#define QSHIFT 4 /* location in qid of client # */ + +#define QID(q) ((((ulong)(q).path)&0x0000000F)>>0) +#define CLIENTPATH(q) ((((ulong)q)&0x7FFFFFF0)>>QSHIFT) +#define CLIENT(q) CLIENTPATH((q).path) + +#define NHASH (1<<5) +#define HASHMASK (NHASH-1) +#define IOUNIT (64*1024) + +typedef struct Client Client; +typedef struct Draw Draw; +typedef struct DImage DImage; +typedef struct DScreen DScreen; +typedef struct CScreen CScreen; +typedef struct FChar FChar; +typedef struct Refresh Refresh; +typedef struct Refx Refx; +typedef struct DName DName; + +ulong blanktime = 30; /* in minutes; a half hour */ + +struct Draw +{ + QLock lk; + int clientid; + int nclient; + Client** client; + int nname; + DName* name; + int vers; + int softscreen; + int blanked; /* screen turned off */ + ulong blanktime; /* time of last operation */ + ulong savemap[3*256]; +}; + +struct Client +{ + Ref r; + DImage* dimage[NHASH]; + CScreen* cscreen; + Refresh* refresh; + Rendez refrend; + uchar* readdata; + int nreaddata; + int busy; + int clientid; + int slot; + int refreshme; + int infoid; + int op; +}; + +struct Refresh +{ + DImage* dimage; + Rectangle r; + Refresh* next; +}; + +struct Refx +{ + Client* client; + DImage* dimage; +}; + +struct DName +{ + char *name; + Client *client; + DImage* dimage; + int vers; +}; + +struct FChar +{ + int minx; /* left edge of bits */ + int maxx; /* right edge of bits */ + uchar miny; /* first non-zero scan-line */ + uchar maxy; /* last non-zero scan-line + 1 */ + schar left; /* offset of baseline */ + uchar width; /* width of baseline */ +}; + +/* + * Reference counts in DImages: + * one per open by original client + * one per screen image or fill + * one per image derived from this one by name + */ +struct DImage +{ + int id; + int ref; + char *name; + int vers; + Memimage* image; + int ascent; + int nfchar; + FChar* fchar; + DScreen* dscreen; /* 0 if not a window */ + DImage* fromname; /* image this one is derived from, by name */ + DImage* next; +}; + +struct CScreen +{ + DScreen* dscreen; + CScreen* next; +}; + +struct DScreen +{ + int id; + int public; + int ref; + DImage *dimage; + DImage *dfill; + Memscreen* screen; + Client* owner; + DScreen* next; +}; + +static Draw sdraw; +static Memimage *screenimage; +static Memdata screendata; +static Rectangle flushrect; +static int waste; +static DScreen* dscreen; +extern void flushmemscreen(Rectangle); + void drawmesg(Client*, void*, int); + void drawuninstall(Client*, int); + void drawfreedimage(DImage*); + Client* drawclientofpath(ulong); + +static char Enodrawimage[] = "unknown id for draw image"; +static char Enodrawscreen[] = "unknown id for draw screen"; +static char Eshortdraw[] = "short draw message"; +static char Eshortread[] = "draw read too short"; +static char Eimageexists[] = "image id in use"; +static char Escreenexists[] = "screen id in use"; +static char Edrawmem[] = "image memory allocation failed"; +static char Ereadoutside[] = "readimage outside image"; +static char Ewriteoutside[] = "writeimage outside image"; +static char Enotfont[] = "image not a font"; +static char Eindex[] = "character index out of range"; +static char Enoclient[] = "no such draw client"; +/* static char Edepth[] = "image has bad depth"; */ +static char Enameused[] = "image name in use"; +static char Enoname[] = "no image with that name"; +static char Eoldname[] = "named image no longer valid"; +static char Enamed[] = "image already has name"; +static char Ewrongname[] = "wrong name for image"; + +int +drawcanqlock(void) +{ + return canqlock(&sdraw.lk); +} + +void +drawqlock(void) +{ + qlock(&sdraw.lk); +} + +void +drawqunlock(void) +{ + qunlock(&sdraw.lk); +} + +static int +drawgen(Chan *c, char *name, Dirtab *dt, int ndt, int s, Dir *dp) +{ + int t; + Qid q; + ulong path; + Client *cl; + + USED(name); + USED(dt); + USED(ndt); + + q.vers = 0; + + if(s == DEVDOTDOT){ + switch(QID(c->qid)){ + case Qtopdir: + case Q2nd: + mkqid(&q, Qtopdir, 0, QTDIR); + devdir(c, q, "#i", 0, eve, 0500, dp); + break; + case Q3rd: + cl = drawclientofpath(c->qid.path); + if(cl == nil) + strcpy(up->genbuf, "??"); + else + sprint(up->genbuf, "%d", cl->clientid); + mkqid(&q, Q2nd, 0, QTDIR); + devdir(c, q, up->genbuf, 0, eve, 0500, dp); + break; + default: + panic("drawwalk %llux", c->qid.path); + } + return 1; + } + + /* + * Top level directory contains the name of the device. + */ + t = QID(c->qid); + if(t == Qtopdir){ + switch(s){ + case 0: + mkqid(&q, Q2nd, 0, QTDIR); + devdir(c, q, "draw", 0, eve, 0555, dp); + break; + default: + return -1; + } + return 1; + } + + /* + * Second level contains "new" plus all the clients. + */ + if(t == Q2nd || t == Qnew){ + if(s == 0){ + mkqid(&q, Qnew, 0, QTFILE); + devdir(c, q, "new", 0, eve, 0666, dp); + } + else if(s <= sdraw.nclient){ + cl = sdraw.client[s-1]; + if(cl == 0) + return 0; + sprint(up->genbuf, "%d", cl->clientid); + mkqid(&q, (s<<QSHIFT)|Q3rd, 0, QTDIR); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + else + return -1; + return 1; + } + + /* + * Third level. + */ + path = c->qid.path&~((1<<QSHIFT)-1); /* slot component */ + q.vers = c->qid.vers; + q.type = QTFILE; + switch(s){ + case 0: + q.path = path|Qcolormap; + devdir(c, q, "colormap", 0, eve, 0600, dp); + break; + case 1: + q.path = path|Qctl; + devdir(c, q, "ctl", 0, eve, 0600, dp); + break; + case 2: + q.path = path|Qdata; + devdir(c, q, "data", 0, eve, 0600, dp); + break; + case 3: + q.path = path|Qrefresh; + devdir(c, q, "refresh", 0, eve, 0400, dp); + break; + default: + return -1; + } + return 1; +} + +static +int +drawrefactive(void *a) +{ + Client *c; + + c = a; + return c->refreshme || c->refresh!=0; +} + +static +void +drawrefreshscreen(DImage *l, Client *client) +{ + while(l != nil && l->dscreen == nil) + l = l->fromname; + if(l != nil && l->dscreen->owner != client) + l->dscreen->owner->refreshme = 1; +} + +static +void +drawrefresh(Memimage *m, Rectangle r, void *v) +{ + Refx *x; + DImage *d; + Client *c; + Refresh *ref; + + USED(m); + + if(v == 0) + return; + x = v; + c = x->client; + d = x->dimage; + for(ref=c->refresh; ref; ref=ref->next) + if(ref->dimage == d){ + combinerect(&ref->r, r); + return; + } + ref = malloc(sizeof(Refresh)); + if(ref){ + ref->dimage = d; + ref->r = r; + ref->next = c->refresh; + c->refresh = ref; + } +} + +static void +addflush(Rectangle r) +{ + int abb, ar, anbb; + Rectangle nbb; + + if(sdraw.softscreen==0 || !rectclip(&r, screenimage->r)) + return; + + if(flushrect.min.x >= flushrect.max.x){ + flushrect = r; + waste = 0; + return; + } + nbb = flushrect; + combinerect(&nbb, r); + ar = Dx(r)*Dy(r); + abb = Dx(flushrect)*Dy(flushrect); + anbb = Dx(nbb)*Dy(nbb); + /* + * Area of new waste is area of new bb minus area of old bb, + * less the area of the new segment, which we assume is not waste. + * This could be negative, but that's OK. + */ + waste += anbb-abb - ar; + if(waste < 0) + waste = 0; + /* + * absorb if: + * total area is small + * waste is less than half total area + * rectangles touch + */ + if(anbb<=1024 || waste*2<anbb || rectXrect(flushrect, r)){ + flushrect = nbb; + return; + } + /* emit current state */ + if(flushrect.min.x < flushrect.max.x) + flushmemscreen(flushrect); + flushrect = r; + waste = 0; +} + +static +void +dstflush(int dstid, Memimage *dst, Rectangle r) +{ + Memlayer *l; + + if(dstid == 0){ + combinerect(&flushrect, r); + return; + } + /* how can this happen? -rsc, dec 12 2002 */ + if(dst == 0){ + print("nil dstflush\n"); + return; + } + l = dst->layer; + if(l == nil) + return; + do{ + if(l->screen->image->data != screenimage->data) + return; + r = rectaddpt(r, l->delta); + l = l->screen->image->layer; + }while(l); + addflush(r); +} + +void +drawflush(void) +{ + if(flushrect.min.x < flushrect.max.x) + flushmemscreen(flushrect); + flushrect = Rect(10000, 10000, -10000, -10000); +} + +void +drawflushr(Rectangle r) +{ + qlock(&sdraw.lk); + flushmemscreen(r); + qunlock(&sdraw.lk); +} + +static +int +drawcmp(char *a, char *b, int n) +{ + if(strlen(a) != n) + return 1; + return memcmp(a, b, n); +} + +DName* +drawlookupname(int n, char *str) +{ + DName *name, *ename; + + name = sdraw.name; + ename = &name[sdraw.nname]; + for(; name<ename; name++) + if(drawcmp(name->name, str, n) == 0) + return name; + return 0; +} + +int +drawgoodname(DImage *d) +{ + DName *n; + + /* if window, validate the screen's own images */ + if(d->dscreen) + if(drawgoodname(d->dscreen->dimage) == 0 + || drawgoodname(d->dscreen->dfill) == 0) + return 0; + if(d->name == nil) + return 1; + n = drawlookupname(strlen(d->name), d->name); + if(n==nil || n->vers!=d->vers) + return 0; + return 1; +} + +DImage* +drawlookup(Client *client, int id, int checkname) +{ + DImage *d; + + d = client->dimage[id&HASHMASK]; + while(d){ + if(d->id == id){ + if(checkname && !drawgoodname(d)) + error(Eoldname); + return d; + } + d = d->next; + } + return 0; +} + +DScreen* +drawlookupdscreen(int id) +{ + DScreen *s; + + s = dscreen; + while(s){ + if(s->id == id) + return s; + s = s->next; + } + return 0; +} + +DScreen* +drawlookupscreen(Client *client, int id, CScreen **cs) +{ + CScreen *s; + + s = client->cscreen; + while(s){ + if(s->dscreen->id == id){ + *cs = s; + return s->dscreen; + } + s = s->next; + } + error(Enodrawscreen); + return 0; +} + +Memimage* +drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen) +{ + DImage *d; + + d = malloc(sizeof(DImage)); + if(d == 0) + return 0; + d->id = id; + d->ref = 1; + d->name = 0; + d->vers = 0; + d->image = i; + d->nfchar = 0; + d->fchar = 0; + d->fromname = 0; + d->dscreen = dscreen; + d->next = client->dimage[id&HASHMASK]; + client->dimage[id&HASHMASK] = d; + return i; +} + +Memscreen* +drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *dfill, int public) +{ + Memscreen *s; + CScreen *c; + + c = malloc(sizeof(CScreen)); + if(dimage && dimage->image && dimage->image->chan == 0) + panic("bad image %p in drawinstallscreen", dimage->image); + + if(c == 0) + return 0; + if(d == 0){ + d = malloc(sizeof(DScreen)); + if(d == 0){ + free(c); + return 0; + } + s = malloc(sizeof(Memscreen)); + if(s == 0){ + free(c); + free(d); + return 0; + } + s->frontmost = 0; + s->rearmost = 0; + d->dimage = dimage; + if(dimage){ + s->image = dimage->image; + dimage->ref++; + } + d->dfill = dfill; + if(dfill){ + s->fill = dfill->image; + dfill->ref++; + } + d->ref = 0; + d->id = id; + d->screen = s; + d->public = public; + d->next = dscreen; + d->owner = client; + dscreen = d; + } + c->dscreen = d; + d->ref++; + c->next = client->cscreen; + client->cscreen = c; + return d->screen; +} + +void +drawdelname(DName *name) +{ + int i; + + i = name-sdraw.name; + memmove(name, name+1, (sdraw.nname-(i+1))*sizeof(DName)); + sdraw.nname--; +} + +void +drawfreedscreen(DScreen *this) +{ + DScreen *ds, *next; + + this->ref--; + if(this->ref < 0) + print("negative ref in drawfreedscreen\n"); + if(this->ref > 0) + return; + ds = dscreen; + if(ds == this){ + dscreen = this->next; + goto Found; + } + while((next = ds->next)){ /* assign = */ + if(next == this){ + ds->next = this->next; + goto Found; + } + ds = next; + } + error(Enodrawimage); + + Found: + if(this->dimage) + drawfreedimage(this->dimage); + if(this->dfill) + drawfreedimage(this->dfill); + free(this->screen); + free(this); +} + +void +drawfreedimage(DImage *dimage) +{ + int i; + Memimage *l; + DScreen *ds; + + dimage->ref--; + if(dimage->ref < 0) + print("negative ref in drawfreedimage\n"); + if(dimage->ref > 0) + return; + + /* any names? */ + for(i=0; i<sdraw.nname; ) + if(sdraw.name[i].dimage == dimage) + drawdelname(sdraw.name+i); + else + i++; + if(dimage->fromname){ /* acquired by name; owned by someone else*/ + drawfreedimage(dimage->fromname); + goto Return; + } + if(dimage->image == screenimage) /* don't free the display */ + goto Return; + ds = dimage->dscreen; + if(ds){ + l = dimage->image; + if(l->data == screenimage->data) + addflush(l->layer->screenr); + if(l->layer->refreshfn == drawrefresh) /* else true owner will clean up */ + free(l->layer->refreshptr); + l->layer->refreshptr = nil; + if(drawgoodname(dimage)) + memldelete(l); + else + memlfree(l); + drawfreedscreen(ds); + }else + freememimage(dimage->image); + Return: + free(dimage->fchar); + free(dimage); +} + +void +drawuninstallscreen(Client *client, CScreen *this) +{ + CScreen *cs, *next; + + cs = client->cscreen; + if(cs == this){ + client->cscreen = this->next; + drawfreedscreen(this->dscreen); + free(this); + return; + } + while((next = cs->next)){ /* assign = */ + if(next == this){ + cs->next = this->next; + drawfreedscreen(this->dscreen); + free(this); + return; + } + cs = next; + } +} + +void +drawuninstall(Client *client, int id) +{ + DImage *d, *next; + + d = client->dimage[id&HASHMASK]; + if(d == 0) + error(Enodrawimage); + if(d->id == id){ + client->dimage[id&HASHMASK] = d->next; + drawfreedimage(d); + return; + } + while((next = d->next)){ /* assign = */ + if(next->id == id){ + d->next = next->next; + drawfreedimage(next); + return; + } + d = next; + } + error(Enodrawimage); +} + +void +drawaddname(Client *client, DImage *di, int n, char *str) +{ + DName *name, *ename, *new, *t; + + name = sdraw.name; + ename = &name[sdraw.nname]; + for(; name<ename; name++) + if(drawcmp(name->name, str, n) == 0) + error(Enameused); + t = smalloc((sdraw.nname+1)*sizeof(DName)); + memmove(t, sdraw.name, sdraw.nname*sizeof(DName)); + free(sdraw.name); + sdraw.name = t; + new = &sdraw.name[sdraw.nname++]; + new->name = smalloc(n+1); + memmove(new->name, str, n); + new->name[n] = 0; + new->dimage = di; + new->client = client; + new->vers = ++sdraw.vers; +} + +Client* +drawnewclient(void) +{ + Client *cl, **cp; + int i; + + for(i=0; i<sdraw.nclient; i++){ + cl = sdraw.client[i]; + if(cl == 0) + break; + } + if(i == sdraw.nclient){ + cp = malloc((sdraw.nclient+1)*sizeof(Client*)); + if(cp == 0) + return 0; + memmove(cp, sdraw.client, sdraw.nclient*sizeof(Client*)); + free(sdraw.client); + sdraw.client = cp; + sdraw.nclient++; + cp[i] = 0; + } + cl = malloc(sizeof(Client)); + if(cl == 0) + return 0; + memset(cl, 0, sizeof(Client)); + cl->slot = i; + cl->clientid = ++sdraw.clientid; + cl->op = SoverD; + sdraw.client[i] = cl; + return cl; +} + +static int +drawclientop(Client *cl) +{ + int op; + + op = cl->op; + cl->op = SoverD; + return op; +} + +int +drawhasclients(void) +{ + /* + * if draw has ever been used, we can't resize the frame buffer, + * even if all clients have exited (nclients is cumulative); it's too + * hard to make work. + */ + return sdraw.nclient != 0; +} + +Client* +drawclientofpath(ulong path) +{ + Client *cl; + int slot; + + slot = CLIENTPATH(path); + if(slot == 0) + return nil; + cl = sdraw.client[slot-1]; + if(cl==0 || cl->clientid==0) + return nil; + return cl; +} + + +Client* +drawclient(Chan *c) +{ + Client *client; + + client = drawclientofpath(c->qid.path); + if(client == nil) + error(Enoclient); + return client; +} + +Memimage* +drawimage(Client *client, uchar *a) +{ + DImage *d; + + d = drawlookup(client, BGLONG(a), 1); + if(d == nil) + error(Enodrawimage); + return d->image; +} + +void +drawrectangle(Rectangle *r, uchar *a) +{ + r->min.x = BGLONG(a+0*4); + r->min.y = BGLONG(a+1*4); + r->max.x = BGLONG(a+2*4); + r->max.y = BGLONG(a+3*4); +} + +void +drawpoint(Point *p, uchar *a) +{ + p->x = BGLONG(a+0*4); + p->y = BGLONG(a+1*4); +} + +#define isvgascreen(dst) 1 + + +Point +drawchar(Memimage *dst, Memimage *rdst, Point p, + Memimage *src, Point *sp, DImage *font, int index, int op) +{ + FChar *fc; + Rectangle r; + Point sp1; + static Memimage *tmp; + + fc = &font->fchar[index]; + r.min.x = p.x+fc->left; + r.min.y = p.y-(font->ascent-fc->miny); + r.max.x = r.min.x+(fc->maxx-fc->minx); + r.max.y = r.min.y+(fc->maxy-fc->miny); + sp1.x = sp->x+fc->left; + sp1.y = sp->y+fc->miny; + + /* + * If we're drawing greyscale fonts onto a VGA screen, + * it's very costly to read the screen memory to do the + * alpha blending inside memdraw. If this is really a stringbg, + * then rdst is the bg image (in main memory) which we can + * refer to for the underlying dst pixels instead of reading dst + * directly. + */ + if(1 || (isvgascreen(dst) && !isvgascreen(rdst) /*&& font->image->depth > 1*/)){ + if(tmp == nil || tmp->chan != dst->chan || Dx(tmp->r) < Dx(r) || Dy(tmp->r) < Dy(r)){ + if(tmp) + freememimage(tmp); + tmp = allocmemimage(Rect(0,0,Dx(r),Dy(r)), dst->chan); + if(tmp == nil) + goto fallback; + } + memdraw(tmp, Rect(0,0,Dx(r),Dy(r)), rdst, r.min, memopaque, ZP, S); + memdraw(tmp, Rect(0,0,Dx(r),Dy(r)), src, sp1, font->image, Pt(fc->minx, fc->miny), op); + memdraw(dst, r, tmp, ZP, memopaque, ZP, S); + }else{ + fallback: + memdraw(dst, r, src, sp1, font->image, Pt(fc->minx, fc->miny), op); + } + + p.x += fc->width; + sp->x += fc->width; + return p; +} + +static int +initscreenimage(void) +{ + int width, depth; + ulong chan; + void *X; + Rectangle r; + + if(screenimage != nil) + return 1; + + screendata.base = nil; + screendata.bdata = attachscreen(&r, &chan, &depth, &width, &sdraw.softscreen, &X); + if(screendata.bdata == nil && X == nil) + return 0; + screendata.ref = 1; + + screenimage = allocmemimaged(r, chan, &screendata, X); + if(screenimage == nil){ + /* RSC: BUG: detach screen */ + return 0; + } + + screenimage->width = width; + screenimage->clipr = r; + return 1; +} + +void +deletescreenimage(void) +{ + qlock(&sdraw.lk); + /* RSC: BUG: detach screen */ + if(screenimage) + freememimage(screenimage); + screenimage = nil; + qunlock(&sdraw.lk); +} + +static Chan* +drawattach(char *spec) +{ + qlock(&sdraw.lk); + if(!initscreenimage()){ + qunlock(&sdraw.lk); + error("no frame buffer"); + } + qunlock(&sdraw.lk); + return devattach('i', spec); +} + +static Walkqid* +drawwalk(Chan *c, Chan *nc, char **name, int nname) +{ + if(screendata.bdata == nil) + error("no frame buffer"); + return devwalk(c, nc, name, nname, 0, 0, drawgen); +} + +static int +drawstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, drawgen); +} + +static Chan* +drawopen(Chan *c, int omode) +{ + Client *cl; + + if(c->qid.type & QTDIR){ + c = devopen(c, omode, 0, 0, drawgen); + c->iounit = IOUNIT; + } + + qlock(&sdraw.lk); + if(waserror()){ + qunlock(&sdraw.lk); + nexterror(); + } + + if(QID(c->qid) == Qnew){ + cl = drawnewclient(); + if(cl == 0) + error(Enodev); + c->qid.path = Qctl|((cl->slot+1)<<QSHIFT); + } + + switch(QID(c->qid)){ + case Qnew: + break; + + case Qctl: + cl = drawclient(c); + if(cl->busy) + error(Einuse); + cl->busy = 1; + flushrect = Rect(10000, 10000, -10000, -10000); + drawinstall(cl, 0, screenimage, 0); + incref(&cl->r); + break; + case Qcolormap: + case Qdata: + case Qrefresh: + cl = drawclient(c); + incref(&cl->r); + break; + } + qunlock(&sdraw.lk); + poperror(); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + c->iounit = IOUNIT; + return c; +} + +static void +drawclose(Chan *c) +{ + int i; + DImage *d, **dp; + Client *cl; + Refresh *r; + + if(QID(c->qid) < Qcolormap) /* Qtopdir, Qnew, Q3rd, Q2nd have no client */ + return; + qlock(&sdraw.lk); + if(waserror()){ + qunlock(&sdraw.lk); + nexterror(); + } + + cl = drawclient(c); + if(QID(c->qid) == Qctl) + cl->busy = 0; + if((c->flag&COPEN) && (decref(&cl->r)==0)){ + while((r = cl->refresh)){ /* assign = */ + cl->refresh = r->next; + free(r); + } + /* free names */ + for(i=0; i<sdraw.nname; ) + if(sdraw.name[i].client == cl) + drawdelname(sdraw.name+i); + else + i++; + while(cl->cscreen) + drawuninstallscreen(cl, cl->cscreen); + /* all screens are freed, so now we can free images */ + dp = cl->dimage; + for(i=0; i<NHASH; i++){ + while((d = *dp) != nil){ + *dp = d->next; + drawfreedimage(d); + } + dp++; + } + sdraw.client[cl->slot] = 0; + drawflush(); /* to erase visible, now dead windows */ + free(cl); + } + qunlock(&sdraw.lk); + poperror(); +} + +long +drawread(Chan *c, void *a, long n, vlong off) +{ + int index, m; + ulong red, green, blue; + Client *cl; + uchar *p; + Refresh *r; + DImage *di; + Memimage *i; + ulong offset = off; + char buf[16]; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, drawgen); + cl = drawclient(c); + qlock(&sdraw.lk); + if(waserror()){ + qunlock(&sdraw.lk); + nexterror(); + } + switch(QID(c->qid)){ + case Qctl: + if(n < 12*12) + error(Eshortread); + if(cl->infoid < 0) + error(Enodrawimage); + if(cl->infoid == 0){ + i = screenimage; + if(i == nil) + error(Enodrawimage); + }else{ + di = drawlookup(cl, cl->infoid, 1); + if(di == nil) + error(Enodrawimage); + i = di->image; + } + n = sprint(a, "%11d %11d %11s %11d %11d %11d %11d %11d %11d %11d %11d %11d ", + cl->clientid, cl->infoid, chantostr(buf, i->chan), (i->flags&Frepl)==Frepl, + i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y, + i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y); + cl->infoid = -1; + break; + + case Qcolormap: + drawactive(1); /* to restore map from backup */ + p = malloc(4*12*256+1); + if(p == 0) + error(Enomem); + m = 0; + for(index = 0; index < 256; index++){ + getcolor(index, &red, &green, &blue); + m += sprint((char*)p+m, "%11d %11lud %11lud %11lud\n", index, red>>24, green>>24, blue>>24); + } + n = readstr(offset, a, n, (char*)p); + free(p); + break; + + case Qdata: + if(cl->readdata == nil) + error("no draw data"); + if(n < cl->nreaddata) + error(Eshortread); + n = cl->nreaddata; + memmove(a, cl->readdata, cl->nreaddata); + free(cl->readdata); + cl->readdata = nil; + break; + + case Qrefresh: + if(n < 5*4) + error(Ebadarg); + for(;;){ + if(cl->refreshme || cl->refresh) + break; + qunlock(&sdraw.lk); + if(waserror()){ + qlock(&sdraw.lk); /* restore lock for waserror() above */ + nexterror(); + } + sleep(&cl->refrend, drawrefactive, cl); + poperror(); + qlock(&sdraw.lk); + } + p = a; + while(cl->refresh && n>=5*4){ + r = cl->refresh; + BPLONG(p+0*4, r->dimage->id); + BPLONG(p+1*4, r->r.min.x); + BPLONG(p+2*4, r->r.min.y); + BPLONG(p+3*4, r->r.max.x); + BPLONG(p+4*4, r->r.max.y); + cl->refresh = r->next; + free(r); + p += 5*4; + n -= 5*4; + } + cl->refreshme = 0; + n = p-(uchar*)a; + } + qunlock(&sdraw.lk); + poperror(); + return n; +} + +void +drawwakeall(void) +{ + Client *cl; + int i; + + for(i=0; i<sdraw.nclient; i++){ + cl = sdraw.client[i]; + if(cl && (cl->refreshme || cl->refresh)) + wakeup(&cl->refrend); + } +} + +static long +drawwrite(Chan *c, void *a, long n, vlong offset) +{ + char buf[128], *fields[4], *q; + Client *cl; + int i, m, red, green, blue, x; + + USED(offset); + + if(c->qid.type & QTDIR) + error(Eisdir); + cl = drawclient(c); + qlock(&sdraw.lk); + if(waserror()){ + drawwakeall(); + qunlock(&sdraw.lk); + nexterror(); + } + switch(QID(c->qid)){ + case Qctl: + if(n != 4) + error("unknown draw control request"); + cl->infoid = BGLONG((uchar*)a); + break; + + case Qcolormap: + drawactive(1); /* to restore map from backup */ + m = n; + n = 0; + while(m > 0){ + x = m; + if(x > sizeof(buf)-1) + x = sizeof(buf)-1; + q = memccpy(buf, a, '\n', x); + if(q == 0) + break; + i = q-buf; + n += i; + a = (char*)a + i; + m -= i; + *q = 0; + if(tokenize(buf, fields, nelem(fields)) != 4) + error(Ebadarg); + i = strtoul(fields[0], 0, 0); + red = strtoul(fields[1], 0, 0); + green = strtoul(fields[2], 0, 0); + blue = strtoul(fields[3], &q, 0); + if(fields[3] == q) + error(Ebadarg); + if(red>255 || green>255 || blue>255 || i<0 || i>255) + error(Ebadarg); + red |= red<<8; + red |= red<<16; + green |= green<<8; + green |= green<<16; + blue |= blue<<8; + blue |= blue<<16; + setcolor(i, red, green, blue); + } + break; + + case Qdata: + drawmesg(cl, a, n); + drawwakeall(); + break; + + default: + error(Ebadusefd); + } + qunlock(&sdraw.lk); + poperror(); + return n; +} + +uchar* +drawcoord(uchar *p, uchar *maxp, int oldx, int *newx) +{ + int b, x; + + if(p >= maxp) + error(Eshortdraw); + b = *p++; + x = b & 0x7F; + if(b & 0x80){ + if(p+1 >= maxp) + error(Eshortdraw); + x |= *p++ << 7; + x |= *p++ << 15; + if(x & (1<<22)) + x |= ~0<<23; + }else{ + if(b & 0x40) + x |= ~0<<7; + x += oldx; + } + *newx = x; + return p; +} + +static void +printmesg(char *fmt, uchar *a, int plsprnt) +{ + char buf[256]; + char *p, *q; + int s; + + if(1|| plsprnt==0){ + SET(s); + SET(q); + SET(p); + USED(fmt); + USED(a); + p = buf; + USED(p); + USED(q); + USED(s); + return; + } + q = buf; + *q++ = *a++; + for(p=fmt; *p; p++){ + switch(*p){ + case 'l': + q += sprint(q, " %ld", (long)BGLONG(a)); + a += 4; + break; + case 'L': + q += sprint(q, " %.8lux", (ulong)BGLONG(a)); + a += 4; + break; + case 'R': + q += sprint(q, " [%d %d %d %d]", BGLONG(a), BGLONG(a+4), BGLONG(a+8), BGLONG(a+12)); + a += 16; + break; + case 'P': + q += sprint(q, " [%d %d]", BGLONG(a), BGLONG(a+4)); + a += 8; + break; + case 'b': + q += sprint(q, " %d", *a++); + break; + case 's': + q += sprint(q, " %d", BGSHORT(a)); + a += 2; + break; + case 'S': + q += sprint(q, " %.4ux", BGSHORT(a)); + a += 2; + break; + } + } + *q++ = '\n'; + *q = 0; + iprint("%.*s", (int)(q-buf), buf); +} + +void +drawmesg(Client *client, void *av, int n) +{ + int c, repl, m, y, dstid, scrnid, ni, ci, j, nw, e0, e1, op, ox, oy, oesize, esize, doflush; + uchar *u, *a, refresh; + char *fmt; + ulong value, chan; + Rectangle r, clipr; + Point p, q, *pp, sp; + Memimage *i, *dst, *src, *mask; + Memimage *l, **lp; + Memscreen *scrn; + DImage *font, *ll, *di, *ddst, *dsrc; + DName *dn; + DScreen *dscrn; + FChar *fc; + Refx *refx; + CScreen *cs; + Refreshfn reffn; + + a = av; + m = 0; + fmt = nil; + if(waserror()){ + if(fmt) printmesg(fmt, a, 1); + /* iprint("error: %s\n", up->errstr); */ + nexterror(); + } + while((n-=m) > 0){ + USED(fmt); + a += m; + switch(*a){ + default: + error("bad draw command"); + /* new allocate: 'b' id[4] screenid[4] refresh[1] chan[4] repl[1] R[4*4] clipR[4*4] rrggbbaa[4] */ + case 'b': + printmesg(fmt="LLbLbRRL", a, 0); + m = 1+4+4+1+4+1+4*4+4*4+4; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + scrnid = BGSHORT(a+5); + refresh = a[9]; + chan = BGLONG(a+10); + repl = a[14]; + drawrectangle(&r, a+15); + drawrectangle(&clipr, a+31); + value = BGLONG(a+47); + if(drawlookup(client, dstid, 0)) + error(Eimageexists); + if(scrnid){ + dscrn = drawlookupscreen(client, scrnid, &cs); + scrn = dscrn->screen; + if(repl || chan!=scrn->image->chan) + error("image parameters incompatible with screen"); + reffn = 0; + switch(refresh){ + case Refbackup: + break; + case Refnone: + reffn = memlnorefresh; + break; + case Refmesg: + reffn = drawrefresh; + break; + default: + error("unknown refresh method"); + } + l = memlalloc(scrn, r, reffn, 0, value); + if(l == 0) + error(Edrawmem); + addflush(l->layer->screenr); + l->clipr = clipr; + rectclip(&l->clipr, r); + if(drawinstall(client, dstid, l, dscrn) == 0){ + memldelete(l); + error(Edrawmem); + } + dscrn->ref++; + if(reffn){ + refx = nil; + if(reffn == drawrefresh){ + refx = malloc(sizeof(Refx)); + if(refx == 0){ + drawuninstall(client, dstid); + error(Edrawmem); + } + refx->client = client; + refx->dimage = drawlookup(client, dstid, 1); + } + memlsetrefresh(l, reffn, refx); + } + continue; + } + i = allocmemimage(r, chan); + if(i == 0) + error(Edrawmem); + if(repl) + i->flags |= Frepl; + i->clipr = clipr; + if(!repl) + rectclip(&i->clipr, r); + if(drawinstall(client, dstid, i, 0) == 0){ + freememimage(i); + error(Edrawmem); + } + memfillcolor(i, value); + continue; + + /* allocate screen: 'A' id[4] imageid[4] fillid[4] public[1] */ + case 'A': + printmesg(fmt="LLLb", a, 1); + m = 1+4+4+4+1; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + if(dstid == 0) + error(Ebadarg); + if(drawlookupdscreen(dstid)) + error(Escreenexists); + ddst = drawlookup(client, BGLONG(a+5), 1); + dsrc = drawlookup(client, BGLONG(a+9), 1); + if(ddst==0 || dsrc==0) + error(Enodrawimage); + if(drawinstallscreen(client, 0, dstid, ddst, dsrc, a[13]) == 0) + error(Edrawmem); + continue; + + /* set repl and clip: 'c' dstid[4] repl[1] clipR[4*4] */ + case 'c': + printmesg(fmt="LbR", a, 0); + m = 1+4+1+4*4; + if(n < m) + error(Eshortdraw); + ddst = drawlookup(client, BGLONG(a+1), 1); + if(ddst == nil) + error(Enodrawimage); + if(ddst->name) + error("can't change repl/clipr of shared image"); + dst = ddst->image; + if(a[5]) + dst->flags |= Frepl; + drawrectangle(&dst->clipr, a+6); + continue; + + /* draw: 'd' dstid[4] srcid[4] maskid[4] R[4*4] P[2*4] P[2*4] */ + case 'd': + printmesg(fmt="LLLRPP", a, 0); + m = 1+4+4+4+4*4+2*4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + dstid = BGLONG(a+1); + src = drawimage(client, a+5); + mask = drawimage(client, a+9); + drawrectangle(&r, a+13); + drawpoint(&p, a+29); + drawpoint(&q, a+37); + op = drawclientop(client); + memdraw(dst, r, src, p, mask, q, op); + dstflush(dstid, dst, r); + continue; + + /* toggle debugging: 'D' val[1] */ + case 'D': + printmesg(fmt="b", a, 0); + m = 1+1; + if(n < m) + error(Eshortdraw); + drawdebug = a[1]; + continue; + + /* ellipse: 'e' dstid[4] srcid[4] center[2*4] a[4] b[4] thick[4] sp[2*4] alpha[4] phi[4]*/ + case 'e': + case 'E': + printmesg(fmt="LLPlllPll", a, 0); + m = 1+4+4+2*4+4+4+4+2*4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + dstid = BGLONG(a+1); + src = drawimage(client, a+5); + drawpoint(&p, a+9); + e0 = BGLONG(a+17); + e1 = BGLONG(a+21); + if(e0<0 || e1<0) + error("invalid ellipse semidiameter"); + j = BGLONG(a+25); + if(j < 0) + error("negative ellipse thickness"); + drawpoint(&sp, a+29); + c = j; + if(*a == 'E') + c = -1; + ox = BGLONG(a+37); + oy = BGLONG(a+41); + op = drawclientop(client); + /* high bit indicates arc angles are present */ + if(ox & (1U<<31)){ + if((ox & (1<<30)) == 0) + ox &= ~(1U<<31); + memarc(dst, p, e0, e1, c, src, sp, ox, oy, op); + }else + memellipse(dst, p, e0, e1, c, src, sp, op); + dstflush(dstid, dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1)); + continue; + + /* free: 'f' id[4] */ + case 'f': + printmesg(fmt="L", a, 1); + m = 1+4; + if(n < m) + error(Eshortdraw); + ll = drawlookup(client, BGLONG(a+1), 0); + if(ll && ll->dscreen && ll->dscreen->owner != client) + ll->dscreen->owner->refreshme = 1; + drawuninstall(client, BGLONG(a+1)); + continue; + + /* free screen: 'F' id[4] */ + case 'F': + printmesg(fmt="L", a, 1); + m = 1+4; + if(n < m) + error(Eshortdraw); + drawlookupscreen(client, BGLONG(a+1), &cs); + drawuninstallscreen(client, cs); + continue; + + /* initialize font: 'i' fontid[4] nchars[4] ascent[1] */ + case 'i': + printmesg(fmt="Llb", a, 1); + m = 1+4+4+1; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + if(dstid == 0) + error("can't use display as font"); + font = drawlookup(client, dstid, 1); + if(font == 0) + error(Enodrawimage); + if(font->image->layer) + error("can't use window as font"); + ni = BGLONG(a+5); + if(ni<=0 || ni>4096) + error("bad font size (4096 chars max)"); + free(font->fchar); /* should we complain if non-zero? */ + font->fchar = malloc(ni*sizeof(FChar)); + if(font->fchar == 0) + error("no memory for font"); + memset(font->fchar, 0, ni*sizeof(FChar)); + font->nfchar = ni; + font->ascent = a[9]; + continue; + + /* load character: 'l' fontid[4] srcid[4] index[2] R[4*4] P[2*4] left[1] width[1] */ + case 'l': + printmesg(fmt="LLSRPbb", a, 0); + m = 1+4+4+2+4*4+2*4+1+1; + if(n < m) + error(Eshortdraw); + font = drawlookup(client, BGLONG(a+1), 1); + if(font == 0) + error(Enodrawimage); + if(font->nfchar == 0) + error(Enotfont); + src = drawimage(client, a+5); + ci = BGSHORT(a+9); + if(ci >= font->nfchar) + error(Eindex); + drawrectangle(&r, a+11); + drawpoint(&p, a+27); + memdraw(font->image, r, src, p, memopaque, p, S); + fc = &font->fchar[ci]; + fc->minx = r.min.x; + fc->maxx = r.max.x; + fc->miny = r.min.y; + fc->maxy = r.max.y; + fc->left = a[35]; + fc->width = a[36]; + continue; + + /* draw line: 'L' dstid[4] p0[2*4] p1[2*4] end0[4] end1[4] radius[4] srcid[4] sp[2*4] */ + case 'L': + printmesg(fmt="LPPlllLP", a, 0); + m = 1+4+2*4+2*4+4+4+4+4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + dstid = BGLONG(a+1); + drawpoint(&p, a+5); + drawpoint(&q, a+13); + e0 = BGLONG(a+21); + e1 = BGLONG(a+25); + j = BGLONG(a+29); + if(j < 0) + error("negative line width"); + src = drawimage(client, a+33); + drawpoint(&sp, a+37); + op = drawclientop(client); + memline(dst, p, q, e0, e1, j, src, sp, op); + /* avoid memlinebbox if possible */ + if(dstid==0 || dst->layer!=nil){ + /* BUG: this is terribly inefficient: update maximal containing rect*/ + r = memlinebbox(p, q, e0, e1, j); + dstflush(dstid, dst, insetrect(r, -(1+1+j))); + } + continue; + + /* create image mask: 'm' newid[4] id[4] */ +/* + * + case 'm': + printmesg("LL", a, 0); + m = 4+4; + if(n < m) + error(Eshortdraw); + break; + * + */ + + /* attach to a named image: 'n' dstid[4] j[1] name[j] */ + case 'n': + printmesg(fmt="Lz", a, 0); + m = 1+4+1; + if(n < m) + error(Eshortdraw); + j = a[5]; + if(j == 0) /* give me a non-empty name please */ + error(Eshortdraw); + m += j; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + if(drawlookup(client, dstid, 0)) + error(Eimageexists); + dn = drawlookupname(j, (char*)a+6); + if(dn == nil) + error(Enoname); + if(drawinstall(client, dstid, dn->dimage->image, 0) == 0) + error(Edrawmem); + di = drawlookup(client, dstid, 0); + if(di == 0) + error("draw: can't happen"); + di->vers = dn->vers; + di->name = smalloc(j+1); + di->fromname = dn->dimage; + di->fromname->ref++; + memmove(di->name, a+6, j); + di->name[j] = 0; + client->infoid = dstid; + continue; + + /* name an image: 'N' dstid[4] in[1] j[1] name[j] */ + case 'N': + printmesg(fmt="Lbz", a, 0); + m = 1+4+1+1; + if(n < m) + error(Eshortdraw); + c = a[5]; + j = a[6]; + if(j == 0) /* give me a non-empty name please */ + error(Eshortdraw); + m += j; + if(n < m) + error(Eshortdraw); + di = drawlookup(client, BGLONG(a+1), 0); + if(di == 0) + error(Enodrawimage); + if(di->name) + error(Enamed); + if(c) + drawaddname(client, di, j, (char*)a+7); + else{ + dn = drawlookupname(j, (char*)a+7); + if(dn == nil) + error(Enoname); + if(dn->dimage != di) + error(Ewrongname); + drawdelname(dn); + } + continue; + + /* position window: 'o' id[4] r.min [2*4] screenr.min [2*4] */ + case 'o': + printmesg(fmt="LPP", a, 0); + m = 1+4+2*4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + if(dst->layer){ + drawpoint(&p, a+5); + drawpoint(&q, a+13); + r = dst->layer->screenr; + ni = memlorigin(dst, p, q); + if(ni < 0) + error("image origin failed"); + if(ni > 0){ + addflush(r); + addflush(dst->layer->screenr); + ll = drawlookup(client, BGLONG(a+1), 1); + drawrefreshscreen(ll, client); + } + } + continue; + + /* set compositing operator for next draw operation: 'O' op */ + case 'O': + printmesg(fmt="b", a, 0); + m = 1+1; + if(n < m) + error(Eshortdraw); + client->op = a[1]; + continue; + + /* filled polygon: 'P' dstid[4] n[2] wind[4] ignore[2*4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */ + /* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */ + case 'p': + case 'P': + printmesg(fmt="LslllLPP", a, 0); + m = 1+4+2+4+4+4+4+2*4; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + dst = drawimage(client, a+1); + ni = BGSHORT(a+5); + if(ni < 0) + error("negative count in polygon"); + e0 = BGLONG(a+7); + e1 = BGLONG(a+11); + j = 0; + if(*a == 'p'){ + j = BGLONG(a+15); + if(j < 0) + error("negative polygon line width"); + } + src = drawimage(client, a+19); + drawpoint(&sp, a+23); + drawpoint(&p, a+31); + ni++; + pp = malloc(ni*sizeof(Point)); + if(pp == nil) + error(Enomem); + doflush = 0; + if(dstid==0 || (dst->layer && dst->layer->screen->image->data == screenimage->data)) + doflush = 1; /* simplify test in loop */ + ox = oy = 0; + esize = 0; + u = a+m; + for(y=0; y<ni; y++){ + q = p; + oesize = esize; + u = drawcoord(u, a+n, ox, &p.x); + u = drawcoord(u, a+n, oy, &p.y); + ox = p.x; + oy = p.y; + if(doflush){ + esize = j; + if(*a == 'p'){ + if(y == 0){ + c = memlineendsize(e0); + if(c > esize) + esize = c; + } + if(y == ni-1){ + c = memlineendsize(e1); + if(c > esize) + esize = c; + } + } + if(*a=='P' && e0!=1 && e0 !=~0) + r = dst->clipr; + else if(y > 0){ + r = Rect(q.x-oesize, q.y-oesize, q.x+oesize+1, q.y+oesize+1); + combinerect(&r, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); + } + if(rectclip(&r, dst->clipr)) /* should perhaps be an arg to dstflush */ + dstflush(dstid, dst, r); + } + pp[y] = p; + } + if(y == 1) + dstflush(dstid, dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); + op = drawclientop(client); + if(*a == 'p') + mempoly(dst, pp, ni, e0, e1, j, src, sp, op); + else + memfillpoly(dst, pp, ni, e0, src, sp, op); + free(pp); + m = u-a; + continue; + + /* read: 'r' id[4] R[4*4] */ + case 'r': + printmesg(fmt="LR", a, 0); + m = 1+4+4*4; + if(n < m) + error(Eshortdraw); + i = drawimage(client, a+1); + drawrectangle(&r, a+5); + if(!rectinrect(r, i->r)) + error(Ereadoutside); + c = bytesperline(r, i->depth); + c *= Dy(r); + free(client->readdata); + client->readdata = mallocz(c, 0); + if(client->readdata == nil) + error("readimage malloc failed"); + client->nreaddata = memunload(i, r, client->readdata, c); + if(client->nreaddata < 0){ + free(client->readdata); + client->readdata = nil; + error("bad readimage call"); + } + continue; + + /* string: 's' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] ni*(index[2]) */ + /* stringbg: 'x' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] bgid[4] bgpt[2*4] ni*(index[2]) */ + case 's': + case 'x': + printmesg(fmt="LLLPRPs", a, 0); + m = 1+4+4+4+2*4+4*4+2*4+2; + if(*a == 'x') + m += 4+2*4; + if(n < m) + error(Eshortdraw); + + dst = drawimage(client, a+1); + dstid = BGLONG(a+1); + src = drawimage(client, a+5); + font = drawlookup(client, BGLONG(a+9), 1); + if(font == 0) + error(Enodrawimage); + if(font->nfchar == 0) + error(Enotfont); + drawpoint(&p, a+13); + drawrectangle(&r, a+21); + drawpoint(&sp, a+37); + ni = BGSHORT(a+45); + u = a+m; + m += ni*2; + if(n < m) + error(Eshortdraw); + clipr = dst->clipr; + dst->clipr = r; + op = drawclientop(client); + l = dst; + if(*a == 'x'){ + /* paint background */ + l = drawimage(client, a+47); + drawpoint(&q, a+51); + r.min.x = p.x; + r.min.y = p.y-font->ascent; + r.max.x = p.x; + r.max.y = r.min.y+Dy(font->image->r); + j = ni; + while(--j >= 0){ + ci = BGSHORT(u); + if(ci<0 || ci>=font->nfchar){ + dst->clipr = clipr; + error(Eindex); + } + r.max.x += font->fchar[ci].width; + u += 2; + } + memdraw(dst, r, l, q, memopaque, ZP, op); + u -= 2*ni; + } + q = p; + while(--ni >= 0){ + ci = BGSHORT(u); + if(ci<0 || ci>=font->nfchar){ + dst->clipr = clipr; + error(Eindex); + } + q = drawchar(dst, l, q, src, &sp, font, ci, op); + u += 2; + } + dst->clipr = clipr; + p.y -= font->ascent; + dstflush(dstid, dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r))); + continue; + + /* use public screen: 'S' id[4] chan[4] */ + case 'S': + printmesg(fmt="Ll", a, 0); + m = 1+4+4; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + if(dstid == 0) + error(Ebadarg); + dscrn = drawlookupdscreen(dstid); + if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=client)) + error(Enodrawscreen); + if(dscrn->screen->image->chan != BGLONG(a+5)) + error("inconsistent chan"); + if(drawinstallscreen(client, dscrn, 0, 0, 0, 0) == 0) + error(Edrawmem); + continue; + + /* top or bottom windows: 't' top[1] nw[2] n*id[4] */ + case 't': + printmesg(fmt="bsL", a, 0); + m = 1+1+2; + if(n < m) + error(Eshortdraw); + nw = BGSHORT(a+2); + if(nw < 0) + error(Ebadarg); + if(nw == 0) + continue; + m += nw*4; + if(n < m) + error(Eshortdraw); + lp = malloc(nw*sizeof(Memimage*)); + if(lp == 0) + error(Enomem); + if(waserror()){ + free(lp); + nexterror(); + } + for(j=0; j<nw; j++) + lp[j] = drawimage(client, a+1+1+2+j*4); + if(lp[0]->layer == 0) + error("images are not windows"); + for(j=1; j<nw; j++) + if(lp[j]->layer->screen != lp[0]->layer->screen) + error("images not on same screen"); + if(a[1]) + memltofrontn(lp, nw); + else + memltorearn(lp, nw); + if(lp[0]->layer->screen->image->data == screenimage->data) + for(j=0; j<nw; j++) + addflush(lp[j]->layer->screenr); + ll = drawlookup(client, BGLONG(a+1+1+2), 1); + drawrefreshscreen(ll, client); + poperror(); + free(lp); + continue; + + /* visible: 'v' */ + case 'v': + printmesg(fmt="", a, 0); + m = 1; + drawflush(); + continue; + + /* write: 'y' id[4] R[4*4] data[x*1] */ + /* write from compressed data: 'Y' id[4] R[4*4] data[x*1] */ + case 'y': + case 'Y': + printmesg(fmt="LR", a, 0); + // iprint("load %c\n", *a); + m = 1+4+4*4; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + dst = drawimage(client, a+1); + drawrectangle(&r, a+5); + if(!rectinrect(r, dst->r)) + error(Ewriteoutside); + y = memload(dst, r, a+m, n-m, *a=='Y'); + if(y < 0) + error("bad writeimage call"); + dstflush(dstid, dst, r); + m += y; + continue; + } + } + poperror(); +} + +Dev drawdevtab = { + 'i', + "draw", + + devreset, + devinit, + devshutdown, + drawattach, + drawwalk, + drawstat, + drawopen, + devcreate, + drawclose, + drawread, + devbread, + drawwrite, + devbwrite, + devremove, + devwstat, +}; + +/* + * On 8 bit displays, load the default color map + */ +void +drawcmap(void) +{ + int r, g, b, cr, cg, cb, v; + int num, den; + int i, j; + + drawactive(1); /* to restore map from backup */ + for(r=0,i=0; r!=4; r++) + for(v=0; v!=4; v++,i+=16){ + for(g=0,j=v-r; g!=4; g++) + for(b=0;b!=4;b++,j++){ + den = r; + if(g > den) + den = g; + if(b > den) + den = b; + if(den == 0) /* divide check -- pick grey shades */ + cr = cg = cb = v*17; + else{ + num = 17*(4*den+v); + cr = r*num/den; + cg = g*num/den; + cb = b*num/den; + } + setcolor(i+(j&15), + cr*0x01010101, cg*0x01010101, cb*0x01010101); + } + } +} + +void +drawblankscreen(int blank) +{ + int i, nc; + ulong *p; + + if(blank == sdraw.blanked) + return; + if(!canqlock(&sdraw.lk)) + return; + if(!initscreenimage()){ + qunlock(&sdraw.lk); + return; + } + p = sdraw.savemap; + nc = screenimage->depth > 8 ? 256 : 1<<screenimage->depth; + + /* + * blankscreen uses the hardware to blank the screen + * when possible. to help in cases when it is not possible, + * we set the color map to be all black. + */ + if(blank == 0){ /* turn screen on */ + for(i=0; i<nc; i++, p+=3) + setcolor(i, p[0], p[1], p[2]); + // blankscreen(0); + }else{ /* turn screen off */ + // blankscreen(1); + for(i=0; i<nc; i++, p+=3){ + getcolor(i, &p[0], &p[1], &p[2]); + setcolor(i, 0, 0, 0); + } + } + sdraw.blanked = blank; + qunlock(&sdraw.lk); +} + +/* + * record activity on screen, changing blanking as appropriate + */ +void +drawactive(int active) +{ +/* + if(active){ + drawblankscreen(0); + sdraw.blanktime = MACHP(0)->ticks; + }else{ + if(blanktime && sdraw.blanktime && TK2SEC(MACHP(0)->ticks - sdraw.blanktime)/60 >= blanktime) + drawblankscreen(1); + } +*/ +} + +int +drawidletime(void) +{ + return 0; +/* return TK2SEC(MACHP(0)->ticks - sdraw.blanktime)/60; */ +} + diff --git a/sys/src/cmd/unix/drawterm/kern/devfs-posix.c b/sys/src/cmd/unix/drawterm/kern/devfs-posix.c new file mode 100755 index 000000000..23a7dbc4f --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/devfs-posix.c @@ -0,0 +1,635 @@ +#include "u.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> /* for remove, rename */ +#include <limits.h> + +#ifndef NAME_MAX +# define NAME_MAX 256 +#endif +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + + +typedef struct Ufsinfo Ufsinfo; + +enum +{ + NUID = 256, + NGID = 256, + MAXPATH = 1024, + MAXCOMP = 128 +}; + +struct Ufsinfo +{ + int mode; + int fd; + int uid; + int gid; + DIR* dir; + vlong offset; + QLock oq; + char nextname[NAME_MAX]; +}; + +char *base = "/"; + +static Qid fsqid(char*, struct stat *); +static void fspath(Chan*, char*, char*); +static ulong fsdirread(Chan*, uchar*, int, ulong); +static int fsomode(int); + +/* clumsy hack, but not worse than the Path stuff in the last one */ +static char* +uc2name(Chan *c) +{ + char *s; + + if(c->name == nil) + return "/"; + s = c2name(c); + if(s[0]=='#' && s[1]=='U') + return s+2; + return s; +} + +static char* +lastelem(Chan *c) +{ + char *s, *t; + + s = uc2name(c); + if((t = strrchr(s, '/')) == nil) + return s; + if(t[1] == 0) + return t; + return t+1; +} + +static Chan* +fsattach(char *spec) +{ + Chan *c; + struct stat stbuf; + static int devno; + Ufsinfo *uif; + + if(stat(base, &stbuf) < 0) + error(strerror(errno)); + + c = devattach('U', spec); + + uif = mallocz(sizeof(Ufsinfo), 1); + uif->mode = stbuf.st_mode; + uif->uid = stbuf.st_uid; + uif->gid = stbuf.st_gid; + + c->aux = uif; + c->dev = devno++; + c->qid.type = QTDIR; +/*print("fsattach %s\n", c2name(c));*/ + + return c; +} + +static Chan* +fsclone(Chan *c, Chan *nc) +{ + Ufsinfo *uif; + + uif = mallocz(sizeof(Ufsinfo), 1); + *uif = *(Ufsinfo*)c->aux; + nc->aux = uif; + + return nc; +} + +static int +fswalk1(Chan *c, char *name) +{ + struct stat stbuf; + char path[MAXPATH]; + Ufsinfo *uif; + + fspath(c, name, path); + + /*print("** fs walk '%s' -> %s\n", path, name); */ + + if(stat(path, &stbuf) < 0) + return 0; + + uif = c->aux; + + uif->mode = stbuf.st_mode; + uif->uid = stbuf.st_uid; + uif->gid = stbuf.st_gid; + + c->qid = fsqid(path, &stbuf); + + return 1; +} + +extern Cname* addelem(Cname*, char*); + +static Walkqid* +fswalk(Chan *c, Chan *nc, char **name, int nname) +{ + int i; + Cname *cname; + Walkqid *wq; + + if(nc != nil) + panic("fswalk: nc != nil"); + wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); + nc = devclone(c); + cname = c->name; + incref(&cname->ref); + + fsclone(c, nc); + wq->clone = nc; + for(i=0; i<nname; i++){ + nc->name = cname; + if(fswalk1(nc, name[i]) == 0) + break; + cname = addelem(cname, name[i]); + wq->qid[i] = nc->qid; + } + nc->name = cname; + if(i != nname){ + cclose(nc); + wq->clone = nil; + } + wq->nqid = i; + return wq; +} + +static int +fsstat(Chan *c, uchar *buf, int n) +{ + Dir d; + struct stat stbuf; + char path[MAXPATH]; + + if(n < BIT16SZ) + error(Eshortstat); + + fspath(c, 0, path); + if(stat(path, &stbuf) < 0) + error(strerror(errno)); + + d.name = lastelem(c); + d.uid = "unknown"; + d.gid = "unknown"; + d.muid = "unknown"; + d.qid = c->qid; + d.mode = (c->qid.type<<24)|(stbuf.st_mode&0777); + d.atime = stbuf.st_atime; + d.mtime = stbuf.st_mtime; + d.length = stbuf.st_size; + d.type = 'U'; + d.dev = c->dev; + return convD2M(&d, buf, n); +} + +static Chan* +fsopen(Chan *c, int mode) +{ + char path[MAXPATH]; + int m, isdir; + Ufsinfo *uif; + +/*print("fsopen %s\n", c2name(c));*/ + m = mode & (OTRUNC|3); + switch(m) { + case 0: + break; + case 1: + case 1|16: + break; + case 2: + case 0|16: + case 2|16: + break; + case 3: + break; + default: + error(Ebadarg); + } + + isdir = c->qid.type & QTDIR; + + if(isdir && mode != OREAD) + error(Eperm); + + m = fsomode(m & 3); + c->mode = openmode(mode); + + uif = c->aux; + + fspath(c, 0, path); + if(isdir) { + uif->dir = opendir(path); + if(uif->dir == 0) + error(strerror(errno)); + } + else { + if(mode & OTRUNC) + m |= O_TRUNC; + uif->fd = open(path, m, 0666); + + if(uif->fd < 0) + error(strerror(errno)); + } + uif->offset = 0; + + c->offset = 0; + c->flag |= COPEN; + return c; +} + +static void +fscreate(Chan *c, char *name, int mode, ulong perm) +{ + int fd, m; + char path[MAXPATH]; + struct stat stbuf; + Ufsinfo *uif; + + m = fsomode(mode&3); + + fspath(c, name, path); + + uif = c->aux; + + if(perm & DMDIR) { + if(m) + error(Eperm); + + if(mkdir(path, perm & 0777) < 0) + error(strerror(errno)); + + fd = open(path, 0); + if(fd >= 0) { + chmod(path, perm & 0777); + chown(path, uif->uid, uif->uid); + } + close(fd); + + uif->dir = opendir(path); + if(uif->dir == 0) + error(strerror(errno)); + } + else { + fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0666); + if(fd >= 0) { + if(m != 1) { + close(fd); + fd = open(path, m); + } + chmod(path, perm & 0777); + chown(path, uif->uid, uif->gid); + } + if(fd < 0) + error(strerror(errno)); + uif->fd = fd; + } + + if(stat(path, &stbuf) < 0) + error(strerror(errno)); + c->qid = fsqid(path, &stbuf); + c->offset = 0; + c->flag |= COPEN; + c->mode = openmode(mode); +} + +static void +fsclose(Chan *c) +{ + Ufsinfo *uif; + + uif = c->aux; + + if(c->flag & COPEN) { + if(c->qid.type & QTDIR) + closedir(uif->dir); + else + close(uif->fd); + } + + free(uif); +} + +static long +fsread(Chan *c, void *va, long n, vlong offset) +{ + int fd, r; + Ufsinfo *uif; + +/*print("fsread %s\n", c2name(c));*/ + if(c->qid.type & QTDIR) + return fsdirread(c, va, n, offset); + + uif = c->aux; + qlock(&uif->oq); + if(waserror()) { + qunlock(&uif->oq); + nexterror(); + } + fd = uif->fd; + if(uif->offset != offset) { + r = lseek(fd, offset, 0); + if(r < 0) + error(strerror(errno)); + uif->offset = offset; + } + + n = read(fd, va, n); + if(n < 0) + error(strerror(errno)); + + uif->offset += n; + qunlock(&uif->oq); + poperror(); + + return n; +} + +static long +fswrite(Chan *c, void *va, long n, vlong offset) +{ + int fd, r; + Ufsinfo *uif; + + uif = c->aux; + + qlock(&uif->oq); + if(waserror()) { + qunlock(&uif->oq); + nexterror(); + } + fd = uif->fd; + if(uif->offset != offset) { + r = lseek(fd, offset, 0); + if(r < 0) + error(strerror(errno)); + uif->offset = offset; + } + + n = write(fd, va, n); + if(n < 0) + error(strerror(errno)); + + uif->offset += n; + qunlock(&uif->oq); + poperror(); + + return n; +} + +static void +fsremove(Chan *c) +{ + int n; + char path[MAXPATH]; + + fspath(c, 0, path); + if(c->qid.type & QTDIR) + n = rmdir(path); + else + n = remove(path); + if(n < 0) + error(strerror(errno)); +} + +int +fswstat(Chan *c, uchar *buf, int n) +{ + Dir d; + struct stat stbuf; + char old[MAXPATH], new[MAXPATH]; + char strs[MAXPATH*3], *p; + Ufsinfo *uif; + + if(convM2D(buf, n, &d, strs) != n) + error(Ebadstat); + + fspath(c, 0, old); + if(stat(old, &stbuf) < 0) + error(strerror(errno)); + + uif = c->aux; + + if(d.name[0] && strcmp(d.name, lastelem(c)) != 0) { + fspath(c, 0, old); + strcpy(new, old); + p = strrchr(new, '/'); + strcpy(p+1, d.name); + if(rename(old, new) < 0) + error(strerror(errno)); + } + + fspath(c, 0, old); + if(~d.mode != 0 && (int)(d.mode&0777) != (int)(stbuf.st_mode&0777)) { + if(chmod(old, d.mode&0777) < 0) + error(strerror(errno)); + uif->mode &= ~0777; + uif->mode |= d.mode&0777; + } +/* + p = name2pass(gid, d.gid); + if(p == 0) + error(Eunknown); + + if(p->id != stbuf.st_gid) { + if(chown(old, stbuf.st_uid, p->id) < 0) + error(strerror(errno)); + + uif->gid = p->id; + } +*/ + return n; +} + +static Qid +fsqid(char *p, struct stat *st) +{ + Qid q; + int dev; + ulong h; + static int nqdev; + static uchar *qdev; + + if(qdev == 0) + qdev = mallocz(65536U, 1); + + q.type = 0; + if((st->st_mode&S_IFMT) == S_IFDIR) + q.type = QTDIR; + + dev = st->st_dev & 0xFFFFUL; + if(qdev[dev] == 0) + qdev[dev] = ++nqdev; + + h = 0; + while(*p != '\0') + h += *p++ * 13; + + q.path = (vlong)qdev[dev]<<32; + q.path |= h; + q.vers = st->st_mtime; + + return q; +} + +static void +fspath(Chan *c, char *ext, char *path) +{ + strcpy(path, base); + strcat(path, "/"); + strcat(path, uc2name(c)); + if(ext){ + strcat(path, "/"); + strcat(path, ext); + } + cleanname(path); +} + +static int +isdots(char *name) +{ + if(name[0] != '.') + return 0; + if(name[1] == '\0') + return 1; + if(name[1] != '.') + return 0; + if(name[2] == '\0') + return 1; + return 0; +} + +static int +p9readdir(char *name, Ufsinfo *uif) +{ + struct dirent *de; + + if(uif->nextname[0]){ + strcpy(name, uif->nextname); + uif->nextname[0] = 0; + return 1; + } + + de = readdir(uif->dir); + if(de == NULL) + return 0; + + strcpy(name, de->d_name); + return 1; +} + +static ulong +fsdirread(Chan *c, uchar *va, int count, ulong offset) +{ + int i; + Dir d; + long n; + char de[NAME_MAX]; + struct stat stbuf; + char path[MAXPATH], dirpath[MAXPATH]; + Ufsinfo *uif; + +/*print("fsdirread %s\n", c2name(c));*/ + i = 0; + uif = c->aux; + + errno = 0; + if(uif->offset != offset) { + if(offset != 0) + error("bad offset in fsdirread"); + uif->offset = offset; /* sync offset */ + uif->nextname[0] = 0; + rewinddir(uif->dir); + } + + fspath(c, 0, dirpath); + + while(i+BIT16SZ < count) { + if(!p9readdir(de, uif)) + break; + + if(de[0]==0 || isdots(de)) + continue; + + d.name = de; + sprint(path, "%s/%s", dirpath, de); + memset(&stbuf, 0, sizeof stbuf); + + if(stat(path, &stbuf) < 0) { + /* fprint(2, "dir: bad path %s\n", path); */ + /* but continue... probably a bad symlink */ + } + + d.uid = "unknown"; + d.gid = "unknown"; + d.muid = "unknown"; + d.qid = fsqid(path, &stbuf); + d.mode = (d.qid.type<<24)|(stbuf.st_mode&0777); + d.atime = stbuf.st_atime; + d.mtime = stbuf.st_mtime; + d.length = stbuf.st_size; + d.type = 'U'; + d.dev = c->dev; + n = convD2M(&d, (uchar*)va+i, count-i); + if(n == BIT16SZ){ + strcpy(uif->nextname, de); + break; + } + i += n; + } +/*print("got %d\n", i);*/ + uif->offset += i; + return i; +} + +static int +fsomode(int m) +{ + switch(m) { + case 0: /* OREAD */ + case 3: /* OEXEC */ + return 0; + case 1: /* OWRITE */ + return 1; + case 2: /* ORDWR */ + return 2; + } + error(Ebadarg); + return 0; +} + +Dev fsdevtab = { + 'U', + "fs", + + devreset, + devinit, + devshutdown, + fsattach, + fswalk, + fsstat, + fsopen, + fscreate, + fsclose, + fsread, + devbread, + fswrite, + devbwrite, + fsremove, + fswstat, +}; diff --git a/sys/src/cmd/unix/drawterm/kern/devfs-win32.c b/sys/src/cmd/unix/drawterm/kern/devfs-win32.c new file mode 100755 index 000000000..9997bd027 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/devfs-win32.c @@ -0,0 +1,707 @@ +/* + * Disable Unicode until the calls to FindFirstFile etc + * are changed to use wide character strings. + */ +#undef UNICODE +#include <windows.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#ifndef NAME_MAX +# define NAME_MAX 256 +#endif +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +typedef struct DIR DIR; +typedef struct Ufsinfo Ufsinfo; + +enum +{ + NUID = 256, + NGID = 256, + MAXPATH = 1024, + MAXCOMP = 128 +}; + +struct DIR +{ + HANDLE handle; + char* path; + int index; + WIN32_FIND_DATA wfd; +}; + +struct Ufsinfo +{ + int mode; + int fd; + int uid; + int gid; + DIR* dir; + ulong offset; + QLock oq; + char nextname[NAME_MAX]; +}; + +DIR* opendir(char*); +int readdir(char*, DIR*); +void closedir(DIR*); +void rewinddir(DIR*); + +char *base = "c:/."; + +static Qid fsqid(char*, struct stat *); +static void fspath(Chan*, char*, char*); +// static void fsperm(Chan*, int); +static ulong fsdirread(Chan*, uchar*, int, ulong); +static int fsomode(int); +static int chown(char *path, int uid, int); + +/* clumsy hack, but not worse than the Path stuff in the last one */ +static char* +uc2name(Chan *c) +{ + char *s; + + if(c->name == nil) + return "/"; + s = c2name(c); + if(s[0]=='#' && s[1]=='U') + return s+2; + return s; +} + +static char* +lastelem(Chan *c) +{ + char *s, *t; + + s = uc2name(c); + if((t = strrchr(s, '/')) == nil) + return s; + if(t[1] == 0) + return t; + return t+1; +} + +static Chan* +fsattach(char *spec) +{ + Chan *c; + struct stat stbuf; + static int devno; + Ufsinfo *uif; + + if(stat(base, &stbuf) < 0) + error(strerror(errno)); + + c = devattach('U', spec); + + uif = mallocz(sizeof(Ufsinfo), 1); + uif->gid = stbuf.st_gid; + uif->uid = stbuf.st_uid; + uif->mode = stbuf.st_mode; + + c->aux = uif; + c->dev = devno++; + c->qid.type = QTDIR; +/*print("fsattach %s\n", c2name(c));*/ + + return c; +} + +static Chan* +fsclone(Chan *c, Chan *nc) +{ + Ufsinfo *uif; + + uif = mallocz(sizeof(Ufsinfo), 1); + *uif = *(Ufsinfo*)c->aux; + nc->aux = uif; + + return nc; +} + +static int +fswalk1(Chan *c, char *name) +{ + struct stat stbuf; + char path[MAXPATH]; + Ufsinfo *uif; + + fspath(c, name, path); + + /* print("** fs walk '%s' -> %s\n", path, name); */ + + if(stat(path, &stbuf) < 0) + return 0; + + uif = c->aux; + + uif->gid = stbuf.st_gid; + uif->uid = stbuf.st_uid; + uif->mode = stbuf.st_mode; + + c->qid = fsqid(path, &stbuf); + + return 1; +} + +extern Cname* addelem(Cname*, char*); + +static Walkqid* +fswalk(Chan *c, Chan *nc, char **name, int nname) +{ + int i; + Cname *cname; + Walkqid *wq; + + if(nc != nil) + panic("fswalk: nc != nil"); + wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); + nc = devclone(c); + cname = c->name; + incref(&cname->ref); + + fsclone(c, nc); + wq->clone = nc; + for(i=0; i<nname; i++){ + nc->name = cname; + if(fswalk1(nc, name[i]) == 0) + break; + cname = addelem(cname, name[i]); + wq->qid[i] = nc->qid; + } + nc->name = cname; + if(i != nname){ + cclose(nc); + wq->clone = nil; + } + wq->nqid = i; + return wq; +} + +static int +fsstat(Chan *c, uchar *buf, int n) +{ + Dir d; + struct stat stbuf; + char path[MAXPATH]; + + if(n < BIT16SZ) + error(Eshortstat); + + fspath(c, 0, path); + if(stat(path, &stbuf) < 0) + error(strerror(errno)); + + d.name = lastelem(c); + d.uid = "unknown"; + d.gid = "unknown"; + d.muid = "unknown"; + d.qid = c->qid; + d.mode = (c->qid.type<<24)|(stbuf.st_mode&0777); + d.atime = stbuf.st_atime; + d.mtime = stbuf.st_mtime; + d.length = stbuf.st_size; + d.type = 'U'; + d.dev = c->dev; + return convD2M(&d, buf, n); +} + +static Chan* +fsopen(Chan *c, int mode) +{ + char path[MAXPATH]; + int m, isdir; + Ufsinfo *uif; + +/*print("fsopen %s\n", c2name(c));*/ + m = mode & (OTRUNC|3); + switch(m) { + case 0: + break; + case 1: + case 1|16: + break; + case 2: + case 0|16: + case 2|16: + break; + case 3: + break; + default: + error(Ebadarg); + } + + isdir = c->qid.type & QTDIR; + + if(isdir && mode != OREAD) + error(Eperm); + + m = fsomode(m & 3); + c->mode = openmode(mode); + + uif = c->aux; + + fspath(c, 0, path); + if(isdir) { + uif->dir = opendir(path); + if(uif->dir == 0) + error(strerror(errno)); + } + else { + if(mode & OTRUNC) + m |= O_TRUNC; + uif->fd = open(path, m|_O_BINARY, 0666); + + if(uif->fd < 0) + error(strerror(errno)); + } + uif->offset = 0; + + c->offset = 0; + c->flag |= COPEN; + return c; +} + +static void +fscreate(Chan *c, char *name, int mode, ulong perm) +{ + int fd, m; + char path[MAXPATH]; + struct stat stbuf; + Ufsinfo *uif; + + m = fsomode(mode&3); + + fspath(c, name, path); + + uif = c->aux; + + if(perm & DMDIR) { + if(m) + error(Eperm); + + if(mkdir(path) < 0) + error(strerror(errno)); + + fd = open(path, 0); + if(fd >= 0) { + chmod(path, perm & 0777); + chown(path, uif->uid, uif->uid); + } + close(fd); + + uif->dir = opendir(path); + if(uif->dir == 0) + error(strerror(errno)); + } + else { + fd = open(path, _O_WRONLY|_O_BINARY|_O_CREAT|_O_TRUNC, 0666); + if(fd >= 0) { + if(m != 1) { + close(fd); + fd = open(path, m|_O_BINARY); + } + chmod(path, perm & 0777); + chown(path, uif->uid, uif->gid); + } + if(fd < 0) + error(strerror(errno)); + uif->fd = fd; + } + + if(stat(path, &stbuf) < 0) + error(strerror(errno)); + c->qid = fsqid(path, &stbuf); + c->offset = 0; + c->flag |= COPEN; + c->mode = openmode(mode); +} + +static void +fsclose(Chan *c) +{ + Ufsinfo *uif; + + uif = c->aux; + + if(c->flag & COPEN) { + if(c->qid.type & QTDIR) + closedir(uif->dir); + else + close(uif->fd); + } + + free(uif); +} + +static long +fsread(Chan *c, void *va, long n, vlong offset) +{ + int fd, r; + Ufsinfo *uif; + +/*print("fsread %s\n", c2name(c));*/ + if(c->qid.type & QTDIR) + return fsdirread(c, va, n, offset); + + uif = c->aux; + qlock(&uif->oq); + if(waserror()) { + qunlock(&uif->oq); + nexterror(); + } + fd = uif->fd; + if(uif->offset != offset) { + r = lseek(fd, offset, 0); + if(r < 0) + error(strerror(errno)); + uif->offset = offset; + } + + n = read(fd, va, n); + if(n < 0) + error(strerror(errno)); + + uif->offset += n; + qunlock(&uif->oq); + poperror(); + + return n; +} + +static long +fswrite(Chan *c, void *va, long n, vlong offset) +{ + int fd, r; + Ufsinfo *uif; + + uif = c->aux; + + qlock(&uif->oq); + if(waserror()) { + qunlock(&uif->oq); + nexterror(); + } + fd = uif->fd; + if(uif->offset != offset) { + r = lseek(fd, offset, 0); + if(r < 0) + error(strerror(errno)); + uif->offset = offset; + } + + n = write(fd, va, n); + if(n < 0) + error(strerror(errno)); + + uif->offset += n; + qunlock(&uif->oq); + poperror(); + + return n; +} + +static void +fsremove(Chan *c) +{ + int n; + char path[MAXPATH]; + + fspath(c, 0, path); + if(c->qid.type & QTDIR) + n = rmdir(path); + else + n = remove(path); + if(n < 0) + error(strerror(errno)); +} + +static int +fswstat(Chan *c, uchar *buf, int n) +{ + Dir d; + struct stat stbuf; + char old[MAXPATH], new[MAXPATH]; + char strs[MAXPATH*3], *p; + Ufsinfo *uif; + + if (convM2D(buf, n, &d, strs) != n) + error(Ebadstat); + + fspath(c, 0, old); + if(stat(old, &stbuf) < 0) + error(strerror(errno)); + + uif = c->aux; + +// if(uif->uid != stbuf.st_uid) +// error(Eowner); + + if(d.name[0] && strcmp(d.name, lastelem(c)) != 0) { + fspath(c, 0, old); + strcpy(new, old); + p = strrchr(new, '/'); + strcpy(p+1, d.name); + if(rename(old, new) < 0) + error(strerror(errno)); + } + + fspath(c, 0, old); + if(~d.mode != 0 && (int)(d.mode&0777) != (int)(stbuf.st_mode&0777)) { + if(chmod(old, d.mode&0777) < 0) + error(strerror(errno)); + uif->mode &= ~0777; + uif->mode |= d.mode&0777; + } +/* + p = name2pass(gid, d.gid); + if(p == 0) + error(Eunknown); + + if(p->id != stbuf.st_gid) { + if(chown(old, stbuf.st_uid, p->id) < 0) + error(sys_errlist[errno]); + + uif->gid = p->id; + } +*/ + return n; +} + +static Qid +fsqid(char *p, struct stat *st) +{ + Qid q; + int dev; + ulong h; + static int nqdev; + static uchar *qdev; + + if(qdev == 0) + qdev = mallocz(65536U, 1); + + q.type = 0; + if((st->st_mode&S_IFMT) == S_IFDIR) + q.type = QTDIR; + + dev = st->st_dev & 0xFFFFUL; + if(qdev[dev] == 0) + qdev[dev] = ++nqdev; + + h = 0; + while(*p != '\0') + h += *p++ * 13; + + q.path = (vlong)qdev[dev]<<32; + q.path |= h; + q.vers = st->st_mtime; + + return q; +} + +static void +fspath(Chan *c, char *ext, char *path) +{ + strcpy(path, base); + strcat(path, "/"); + strcat(path, uc2name(c)); + if(ext) { + strcat(path, "/"); + strcat(path, ext); + } + cleanname(path); +} + +static int +isdots(char *name) +{ + if(name[0] != '.') + return 0; + if(name[1] == '\0') + return 1; + if(name[1] != '.') + return 0; + if(name[2] == '\0') + return 1; + return 0; +} + +static int +p9readdir(char *name, Ufsinfo *uif) +{ + if(uif->nextname[0]){ + strcpy(name, uif->nextname); + uif->nextname[0] = 0; + return 1; + } + + return readdir(name, uif->dir); +} + +static ulong +fsdirread(Chan *c, uchar *va, int count, ulong offset) +{ + int i; + Dir d; + long n; + char de[NAME_MAX]; + struct stat stbuf; + char path[MAXPATH], dirpath[MAXPATH]; + Ufsinfo *uif; + +/*print("fsdirread %s\n", c2name(c));*/ + i = 0; + uif = c->aux; + + errno = 0; + if(uif->offset != offset) { + if(offset != 0) + error("bad offset in fsdirread"); + uif->offset = offset; /* sync offset */ + uif->nextname[0] = 0; + rewinddir(uif->dir); + } + + fspath(c, 0, dirpath); + + while(i+BIT16SZ < count) { + if(!p9readdir(de, uif)) + break; + + if(de[0]==0 || isdots(de)) + continue; + + d.name = de; + sprint(path, "%s/%s", dirpath, de); + memset(&stbuf, 0, sizeof stbuf); + + if(stat(path, &stbuf) < 0) { + print("dir: bad path %s\n", path); + /* but continue... probably a bad symlink */ + } + + d.uid = "unknown"; + d.gid = "unknown"; + d.muid = "unknown"; + d.qid = fsqid(path, &stbuf); + d.mode = (d.qid.type<<24)|(stbuf.st_mode&0777); + d.atime = stbuf.st_atime; + d.mtime = stbuf.st_mtime; + d.length = stbuf.st_size; + d.type = 'U'; + d.dev = c->dev; + n = convD2M(&d, (uchar*)va+i, count-i); + if(n == BIT16SZ){ + strcpy(uif->nextname, de); + break; + } + i += n; + } +/*print("got %d\n", i);*/ + uif->offset += i; + return i; +} + +static int +fsomode(int m) +{ + switch(m) { + case 0: /* OREAD */ + case 3: /* OEXEC */ + return 0; + case 1: /* OWRITE */ + return 1; + case 2: /* ORDWR */ + return 2; + } + error(Ebadarg); + return 0; +} +void +closedir(DIR *d) +{ + FindClose(d->handle); + free(d->path); +} + +int +readdir(char *name, DIR *d) +{ + if(d->index != 0) { + if(FindNextFile(d->handle, &d->wfd) == FALSE) + return 0; + } + strcpy(name, (char*)d->wfd.cFileName); + d->index++; + + return 1; +} + +void +rewinddir(DIR *d) +{ + FindClose(d->handle); + d->handle = FindFirstFile(d->path, &d->wfd); + d->index = 0; +} + +static int +chown(char *path, int uid, int perm) +{ +/* panic("chown"); */ + return 0; +} + +DIR* +opendir(char *p) +{ + DIR *d; + char path[MAX_PATH]; + + + snprint(path, sizeof(path), "%s/*.*", p); + + d = mallocz(sizeof(DIR), 1); + if(d == 0) + return 0; + + d->index = 0; + + d->handle = FindFirstFile(path, &d->wfd); + if(d->handle == INVALID_HANDLE_VALUE) { + free(d); + return 0; + } + + d->path = strdup(path); + return d; +} + +Dev fsdevtab = { + 'U', + "fs", + + devreset, + devinit, + devshutdown, + fsattach, + fswalk, + fsstat, + fsopen, + fscreate, + fsclose, + fsread, + devbread, + fswrite, + devbwrite, + fsremove, + fswstat, +}; diff --git a/sys/src/cmd/unix/drawterm/kern/devip-posix.c b/sys/src/cmd/unix/drawterm/kern/devip-posix.c new file mode 100755 index 000000000..7f6171e4e --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/devip-posix.c @@ -0,0 +1,210 @@ +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> + +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include "devip.h" + +#undef listen +#undef accept +#undef bind + +void +osipinit(void) +{ + char buf[1024]; + gethostname(buf, sizeof(buf)); + kstrdup(&sysname, buf); + +} + +int +so_socket(int type) +{ + int fd, one; + + switch(type) { + default: + error("bad protocol type"); + case S_TCP: + type = SOCK_STREAM; + break; + case S_UDP: + type = SOCK_DGRAM; + break; + } + + fd = socket(AF_INET, type, 0); + if(fd < 0) + oserror(); + + one = 1; + if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(one)) > 0){ + oserrstr(); + print("setsockopt: %r"); + } + + return fd; +} + + +void +so_connect(int fd, unsigned long raddr, unsigned short rport) +{ + struct sockaddr_in sin; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + hnputs(&sin.sin_port, rport); + hnputl(&sin.sin_addr.s_addr, raddr); + + if(connect(fd, (struct sockaddr*)&sin, sizeof(sin)) < 0) + oserror(); +} + +void +so_getsockname(int fd, unsigned long *laddr, unsigned short *lport) +{ + socklen_t len; + struct sockaddr_in sin; + + len = sizeof(sin); + if(getsockname(fd, (struct sockaddr*)&sin, &len) < 0) + oserror(); + + if(sin.sin_family != AF_INET || len != sizeof(sin)) + error("not AF_INET"); + + *laddr = nhgetl(&sin.sin_addr.s_addr); + *lport = nhgets(&sin.sin_port); +} + +void +so_listen(int fd) +{ + if(listen(fd, 5) < 0) + oserror(); +} + +int +so_accept(int fd, unsigned long *raddr, unsigned short *rport) +{ + int nfd; + socklen_t len; + struct sockaddr_in sin; + + len = sizeof(sin); + nfd = accept(fd, (struct sockaddr*)&sin, &len); + if(nfd < 0) + oserror(); + + if(sin.sin_family != AF_INET || len != sizeof(sin)) + error("not AF_INET"); + + *raddr = nhgetl(&sin.sin_addr.s_addr); + *rport = nhgets(&sin.sin_port); + return nfd; +} + +void +so_bind(int fd, int su, unsigned short port) +{ + int i, one; + struct sockaddr_in sin; + + one = 1; + if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0){ + oserrstr(); + print("setsockopt: %r"); + } + + if(su) { + for(i = 600; i < 1024; i++) { + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = i; + + if(bind(fd, (struct sockaddr*)&sin, sizeof(sin)) >= 0) + return; + } + oserror(); + } + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + hnputs(&sin.sin_port, port); + + if(bind(fd, (struct sockaddr*)&sin, sizeof(sin)) < 0) + oserror(); +} + +int +so_gethostbyname(char *host, char**hostv, int n) +{ + int i; + char buf[32]; + unsigned char *p; + struct hostent *hp; + + hp = gethostbyname(host); + if(hp == 0) + return 0; + + for(i = 0; hp->h_addr_list[i] && i < n; i++) { + p = (unsigned char*)hp->h_addr_list[i]; + sprint(buf, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + hostv[i] = strdup(buf); + if(hostv[i] == 0) + break; + } + return i; +} + +char* +hostlookup(char *host) +{ + char buf[100]; + uchar *p; + struct hostent *he; + + he = gethostbyname(host); + if(he != 0 && he->h_addr_list[0]) { + p = (uchar*)he->h_addr_list[0]; + sprint(buf, "%ud.%ud.%ud.%ud", p[0], p[1], p[2], p[3]); + } else + strcpy(buf, host); + + return strdup(buf); +} + +int +so_getservbyname(char *service, char *net, char *port) +{ + struct servent *s; + + s = getservbyname(service, net); + if(s == 0) + return -1; + + sprint(port, "%d", nhgets(&s->s_port)); + return 0; +} + +int +so_send(int fd, void *d, int n, int f) +{ + return send(fd, d, n, f); +} + +int +so_recv(int fd, void *d, int n, int f) +{ + return recv(fd, d, n, f); +} diff --git a/sys/src/cmd/unix/drawterm/kern/devip-win32.c b/sys/src/cmd/unix/drawterm/kern/devip-win32.c new file mode 100755 index 000000000..3caa67ad1 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/devip-win32.c @@ -0,0 +1,212 @@ +#include <windows.h> +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include "devip.h" + +#ifdef MSVC +#pragma comment(lib, "wsock32.lib") +#endif + +#undef listen +#undef accept +#undef bind + +void +osipinit(void) +{ + WSADATA wasdat; + char buf[1024]; + + if(WSAStartup(MAKEWORD(1, 1), &wasdat) != 0) + panic("no winsock.dll"); + + gethostname(buf, sizeof(buf)); + kstrdup(&sysname, buf); +} + +int +so_socket(int type) +{ + int fd, one; + + switch(type) { + default: + error("bad protocol type"); + case S_TCP: + type = SOCK_STREAM; + break; + case S_UDP: + type = SOCK_DGRAM; + break; + } + + fd = socket(AF_INET, type, 0); + if(fd < 0) + oserror(); + + one = 1; + if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(one)) > 0){ + oserrstr(); + print("setsockopt: %s\n", up->errstr); + } + + return fd; +} + + +void +so_connect(int fd, unsigned long raddr, unsigned short rport) +{ + struct sockaddr_in sin; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + hnputs(&sin.sin_port, rport); + hnputl(&sin.sin_addr.s_addr, raddr); + + if(connect(fd, (struct sockaddr*)&sin, sizeof(sin)) < 0) + oserror(); +} + +void +so_getsockname(int fd, unsigned long *laddr, unsigned short *lport) +{ + int len; + struct sockaddr_in sin; + + len = sizeof(sin); + if(getsockname(fd, (struct sockaddr*)&sin, &len) < 0) + oserror(); + + if(sin.sin_family != AF_INET || len != sizeof(sin)) + error("not AF_INET"); + + *laddr = nhgetl(&sin.sin_addr.s_addr); + *lport = nhgets(&sin.sin_port); +} + +void +so_listen(int fd) +{ + if(listen(fd, 5) < 0) + oserror(); +} + +int +so_accept(int fd, unsigned long *raddr, unsigned short *rport) +{ + int nfd, len; + struct sockaddr_in sin; + + len = sizeof(sin); + nfd = accept(fd, (struct sockaddr*)&sin, &len); + if(nfd < 0) + oserror(); + + if(sin.sin_family != AF_INET || len != sizeof(sin)) + error("not AF_INET"); + + *raddr = nhgetl(&sin.sin_addr.s_addr); + *rport = nhgets(&sin.sin_port); + return nfd; +} + +void +so_bind(int fd, int su, unsigned short port) +{ + int i, one; + struct sockaddr_in sin; + + one = 1; + if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0){ + oserrstr(); + print("setsockopt: %s", up->errstr); + } + + if(su) { + for(i = 600; i < 1024; i++) { + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = i; + + if(bind(fd, (struct sockaddr*)&sin, sizeof(sin)) >= 0) + return; + } + oserror(); + } + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + hnputs(&sin.sin_port, port); + + if(bind(fd, (struct sockaddr*)&sin, sizeof(sin)) < 0) + oserror(); +} + +int +so_gethostbyname(char *host, char**hostv, int n) +{ + int i; + char buf[32]; + unsigned char *p; + struct hostent *hp; + + hp = gethostbyname(host); + if(hp == 0) + return 0; + + for(i = 0; hp->h_addr_list[i] && i < n; i++) { + p = (unsigned char*)hp->h_addr_list[i]; + sprint(buf, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + hostv[i] = strdup(buf); + if(hostv[i] == 0) + break; + } + return i; +} + +char* +hostlookup(char *host) +{ + char buf[100]; + uchar *p; + HOSTENT *he; + + he = gethostbyname(host); + if(he != 0 && he->h_addr_list[0]) { + p = (uchar*)he->h_addr_list[0]; + sprint(buf, "%ud.%ud.%ud.%ud", p[0], p[1], p[2], p[3]); + } else + strcpy(buf, host); + + return strdup(buf); +} + +int +so_getservbyname(char *service, char *net, char *port) +{ + struct servent *s; + + s = getservbyname(service, net); + if(s == 0) + return -1; + + sprint(port, "%d", nhgets(&s->s_port)); + return 0; +} + +int +so_send(int fd, void *d, int n, int f) +{ + return send(fd, d, n, f); +} + +int +so_recv(int fd, void *d, int n, int f) +{ + return recv(fd, d, n, f); +} diff --git a/sys/src/cmd/unix/drawterm/kern/devip.c b/sys/src/cmd/unix/drawterm/kern/devip.c new file mode 100755 index 000000000..f192aebcb --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/devip.c @@ -0,0 +1,938 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include "devip.h" + +void hnputl(void *p, unsigned long v); +void hnputs(void *p, unsigned short v); +unsigned long nhgetl(void *p); +unsigned short nhgets(void *p); +unsigned long parseip(char *to, char *from); +void csclose(Chan*); +long csread(Chan*, void*, long, vlong); +long cswrite(Chan*, void*, long, vlong); + +void osipinit(void); + +enum +{ + Qtopdir = 1, /* top level directory */ + Qcs, + Qprotodir, /* directory for a protocol */ + Qclonus, + Qconvdir, /* directory for a conversation */ + Qdata, + Qctl, + Qstatus, + Qremote, + Qlocal, + Qlisten, + + MAXPROTO = 4 +}; +#define TYPE(x) ((int)((x).path & 0xf)) +#define CONV(x) ((int)(((x).path >> 4)&0xfff)) +#define PROTO(x) ((int)(((x).path >> 16)&0xff)) +#define QID(p, c, y) (((p)<<16) | ((c)<<4) | (y)) + +typedef struct Proto Proto; +typedef struct Conv Conv; +struct Conv +{ + int x; + Ref r; + int sfd; + int perm; + char owner[KNAMELEN]; + char* state; + ulong laddr; + ushort lport; + ulong raddr; + ushort rport; + int restricted; + char cerr[KNAMELEN]; + Proto* p; +}; + +struct Proto +{ + Lock l; + int x; + int stype; + char name[KNAMELEN]; + int nc; + int maxconv; + Conv** conv; + Qid qid; +}; + +static int np; +static Proto proto[MAXPROTO]; +int eipfmt(Fmt*); + +static Conv* protoclone(Proto*, char*, int); +static void setladdr(Conv*); + +int +ipgen(Chan *c, char *nname, Dirtab *d, int nd, int s, Dir *dp) +{ + Qid q; + Conv *cv; + char *p; + + USED(nname); + q.vers = 0; + q.type = 0; + switch(TYPE(c->qid)) { + case Qtopdir: + if(s >= 1+np) + return -1; + + if(s == 0){ + q.path = QID(s, 0, Qcs); + devdir(c, q, "cs", 0, "network", 0666, dp); + }else{ + s--; + q.path = QID(s, 0, Qprotodir); + q.type = QTDIR; + devdir(c, q, proto[s].name, 0, "network", DMDIR|0555, dp); + } + return 1; + case Qprotodir: + if(s < proto[PROTO(c->qid)].nc) { + cv = proto[PROTO(c->qid)].conv[s]; + sprint(up->genbuf, "%d", s); + q.path = QID(PROTO(c->qid), s, Qconvdir); + q.type = QTDIR; + devdir(c, q, up->genbuf, 0, cv->owner, DMDIR|0555, dp); + return 1; + } + s -= proto[PROTO(c->qid)].nc; + switch(s) { + default: + return -1; + case 0: + p = "clone"; + q.path = QID(PROTO(c->qid), 0, Qclonus); + break; + } + devdir(c, q, p, 0, "network", 0555, dp); + return 1; + case Qconvdir: + cv = proto[PROTO(c->qid)].conv[CONV(c->qid)]; + switch(s) { + default: + return -1; + case 0: + q.path = QID(PROTO(c->qid), CONV(c->qid), Qdata); + devdir(c, q, "data", 0, cv->owner, cv->perm, dp); + return 1; + case 1: + q.path = QID(PROTO(c->qid), CONV(c->qid), Qctl); + devdir(c, q, "ctl", 0, cv->owner, cv->perm, dp); + return 1; + case 2: + p = "status"; + q.path = QID(PROTO(c->qid), CONV(c->qid), Qstatus); + break; + case 3: + p = "remote"; + q.path = QID(PROTO(c->qid), CONV(c->qid), Qremote); + break; + case 4: + p = "local"; + q.path = QID(PROTO(c->qid), CONV(c->qid), Qlocal); + break; + case 5: + p = "listen"; + q.path = QID(PROTO(c->qid), CONV(c->qid), Qlisten); + break; + } + devdir(c, q, p, 0, cv->owner, 0444, dp); + return 1; + } + return -1; +} + +static void +newproto(char *name, int type, int maxconv) +{ + int l; + Proto *p; + + if(np >= MAXPROTO) { + print("no %s: increase MAXPROTO", name); + return; + } + + p = &proto[np]; + strcpy(p->name, name); + p->stype = type; + p->qid.path = QID(np, 0, Qprotodir); + p->qid.type = QTDIR; + p->x = np++; + p->maxconv = maxconv; + l = sizeof(Conv*)*(p->maxconv+1); + p->conv = mallocz(l, 1); + if(p->conv == 0) + panic("no memory"); +} + +void +ipinit(void) +{ + osipinit(); + + newproto("udp", S_UDP, 10); + newproto("tcp", S_TCP, 30); + + fmtinstall('I', eipfmt); + fmtinstall('E', eipfmt); + +} + +Chan * +ipattach(char *spec) +{ + Chan *c; + + c = devattach('I', spec); + c->qid.path = QID(0, 0, Qtopdir); + c->qid.type = QTDIR; + c->qid.vers = 0; + return c; +} + +static Walkqid* +ipwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, ipgen); +} + +int +ipstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, 0, 0, ipgen); +} + +Chan * +ipopen(Chan *c, int omode) +{ + Proto *p; + ulong raddr; + ushort rport; + int perm, sfd; + Conv *cv, *lcv; + + omode &= 3; + perm = 0; + switch(omode) { + case OREAD: + perm = 4; + break; + case OWRITE: + perm = 2; + break; + case ORDWR: + perm = 6; + break; + } + + switch(TYPE(c->qid)) { + default: + break; + case Qtopdir: + case Qprotodir: + case Qconvdir: + case Qstatus: + case Qremote: + case Qlocal: + if(omode != OREAD) + error(Eperm); + break; + case Qclonus: + p = &proto[PROTO(c->qid)]; + cv = protoclone(p, up->user, -1); + if(cv == 0) + error(Enodev); + c->qid.path = QID(p->x, cv->x, Qctl); + c->qid.vers = 0; + break; + case Qdata: + case Qctl: + p = &proto[PROTO(c->qid)]; + lock(&p->l); + cv = p->conv[CONV(c->qid)]; + lock(&cv->r.lk); + if((perm & (cv->perm>>6)) != perm) { + if(strcmp(up->user, cv->owner) != 0 || + (perm & cv->perm) != perm) { + unlock(&cv->r.lk); + unlock(&p->l); + error(Eperm); + } + } + cv->r.ref++; + if(cv->r.ref == 1) { + memmove(cv->owner, up->user, KNAMELEN); + cv->perm = 0660; + } + unlock(&cv->r.lk); + unlock(&p->l); + break; + case Qlisten: + p = &proto[PROTO(c->qid)]; + lcv = p->conv[CONV(c->qid)]; + sfd = so_accept(lcv->sfd, &raddr, &rport); + cv = protoclone(p, up->user, sfd); + if(cv == 0) { + close(sfd); + error(Enodev); + } + cv->raddr = raddr; + cv->rport = rport; + setladdr(cv); + cv->state = "Established"; + c->qid.path = QID(p->x, cv->x, Qctl); + break; + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +void +ipclose(Chan *c) +{ + Conv *cc; + + switch(TYPE(c->qid)) { + case Qcs: + csclose(c); + break; + case Qdata: + case Qctl: + if((c->flag & COPEN) == 0) + break; + cc = proto[PROTO(c->qid)].conv[CONV(c->qid)]; + if(decref(&cc->r) != 0) + break; + strcpy(cc->owner, "network"); + cc->perm = 0666; + cc->state = "Closed"; + cc->laddr = 0; + cc->raddr = 0; + cc->lport = 0; + cc->rport = 0; + close(cc->sfd); + break; + } +} + +long +ipread(Chan *ch, void *a, long n, vlong offset) +{ + int r; + Conv *c; + Proto *x; + uchar ip[4]; + char buf[128], *p; + +/*print("ipread %s %lux\n", c2name(ch), (long)ch->qid.path);*/ + p = a; + switch(TYPE(ch->qid)) { + default: + error(Eperm); + case Qcs: + return csread(ch, a, n, offset); + case Qprotodir: + case Qtopdir: + case Qconvdir: + return devdirread(ch, a, n, 0, 0, ipgen); + case Qctl: + sprint(buf, "%d", CONV(ch->qid)); + return readstr(offset, p, n, buf); + case Qremote: + c = proto[PROTO(ch->qid)].conv[CONV(ch->qid)]; + hnputl(ip, c->raddr); + sprint(buf, "%I!%d\n", ip, c->rport); + return readstr(offset, p, n, buf); + case Qlocal: + c = proto[PROTO(ch->qid)].conv[CONV(ch->qid)]; + hnputl(ip, c->laddr); + sprint(buf, "%I!%d\n", ip, c->lport); + return readstr(offset, p, n, buf); + case Qstatus: + x = &proto[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + sprint(buf, "%s/%d %d %s \n", + c->p->name, c->x, c->r.ref, c->state); + return readstr(offset, p, n, buf); + case Qdata: + c = proto[PROTO(ch->qid)].conv[CONV(ch->qid)]; + r = so_recv(c->sfd, a, n, 0); + if(r < 0){ + oserrstr(); + nexterror(); + } + return r; + } +} + +static void +setladdr(Conv *c) +{ + so_getsockname(c->sfd, &c->laddr, &c->lport); +} + +static void +setlport(Conv *c) +{ + if(c->restricted == 0 && c->lport == 0) + return; + + so_bind(c->sfd, c->restricted, c->lport); +} + +static void +setladdrport(Conv *c, char *str) +{ + char *p, addr[4]; + + p = strchr(str, '!'); + if(p == 0) { + p = str; + c->laddr = 0; + } + else { + *p++ = 0; + parseip(addr, str); + c->laddr = nhgetl((uchar*)addr); + } + if(*p == '*') + c->lport = 0; + else + c->lport = atoi(p); + + setlport(c); +} + +static char* +setraddrport(Conv *c, char *str) +{ + char *p, addr[4]; + + p = strchr(str, '!'); + if(p == 0) + return "malformed address"; + *p++ = 0; + parseip(addr, str); + c->raddr = nhgetl((uchar*)addr); + c->rport = atoi(p); + p = strchr(p, '!'); + if(p) { + if(strcmp(p, "!r") == 0) + c->restricted = 1; + } + return 0; +} + +long +ipwrite(Chan *ch, void *a, long n, vlong offset) +{ + Conv *c; + Proto *x; + int r, nf; + char *p, *fields[3], buf[128]; + + switch(TYPE(ch->qid)) { + default: + error(Eperm); + case Qcs: + return cswrite(ch, a, n, offset); + case Qctl: + x = &proto[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + if(n > sizeof(buf)-1) + n = sizeof(buf)-1; + memmove(buf, a, n); + buf[n] = '\0'; + + nf = tokenize(buf, fields, 3); + if(strcmp(fields[0], "connect") == 0){ + switch(nf) { + default: + error("bad args to connect"); + case 2: + p = setraddrport(c, fields[1]); + if(p != 0) + error(p); + break; + case 3: + p = setraddrport(c, fields[1]); + if(p != 0) + error(p); + c->lport = atoi(fields[2]); + setlport(c); + break; + } + so_connect(c->sfd, c->raddr, c->rport); + setladdr(c); + c->state = "Established"; + return n; + } + if(strcmp(fields[0], "announce") == 0) { + switch(nf){ + default: + error("bad args to announce"); + case 2: + setladdrport(c, fields[1]); + break; + } + so_listen(c->sfd); + c->state = "Announced"; + return n; + } + if(strcmp(fields[0], "bind") == 0){ + switch(nf){ + default: + error("bad args to bind"); + case 2: + c->lport = atoi(fields[1]); + break; + } + setlport(c); + return n; + } + error("bad control message"); + case Qdata: + x = &proto[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + r = so_send(c->sfd, a, n, 0); + if(r < 0){ + oserrstr(); + nexterror(); + } + return r; + } + return n; +} + +static Conv* +protoclone(Proto *p, char *user, int nfd) +{ + Conv *c, **pp, **ep; + + c = 0; + lock(&p->l); + if(waserror()) { + unlock(&p->l); + nexterror(); + } + ep = &p->conv[p->maxconv]; + for(pp = p->conv; pp < ep; pp++) { + c = *pp; + if(c == 0) { + c = mallocz(sizeof(Conv), 1); + if(c == 0) + error(Enomem); + lock(&c->r.lk); + c->r.ref = 1; + c->p = p; + c->x = pp - p->conv; + p->nc++; + *pp = c; + break; + } + lock(&c->r.lk); + if(c->r.ref == 0) { + c->r.ref++; + break; + } + unlock(&c->r.lk); + } + if(pp >= ep) { + unlock(&p->l); + poperror(); + return 0; + } + + strcpy(c->owner, user); + c->perm = 0660; + c->state = "Closed"; + c->restricted = 0; + c->laddr = 0; + c->raddr = 0; + c->lport = 0; + c->rport = 0; + c->sfd = nfd; + if(nfd == -1) + c->sfd = so_socket(p->stype); + + unlock(&c->r.lk); + unlock(&p->l); + poperror(); + return c; +} + +enum +{ + Isprefix= 16, +}; + +uchar prefixvals[256] = +{ +/*0x00*/ 0 | Isprefix, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0x10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0x20*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0x30*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0x40*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0x50*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0x60*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0x70*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0x80*/ 1 | Isprefix, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0x90*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0xA0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0xB0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0xC0*/ 2 | Isprefix, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0xD0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0xE0*/ 3 | Isprefix, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0xF0*/ 4 | Isprefix, + 0, 0, 0, 0, 0, 0, 0, +/*0xF8*/ 5 | Isprefix, + 0, 0, 0, +/*0xFC*/ 6 | Isprefix, + 0, +/*0xFE*/ 7 | Isprefix, +/*0xFF*/ 8 | Isprefix, +}; + +int +eipfmt(Fmt *f) +{ + char buf[5*8]; + static char *efmt = "%.2lux%.2lux%.2lux%.2lux%.2lux%.2lux"; + static char *ifmt = "%d.%d.%d.%d"; + uchar *p, ip[16]; + ulong ul; + + 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': + ul = va_arg(f->args, ulong); + hnputl(ip, ul); + snprint(buf, sizeof buf, ifmt, ip[0], ip[1], ip[2], ip[3]); + return fmtstrcpy(f, buf); + } + return fmtstrcpy(f, "(eipfmt)"); +} + +void +hnputl(void *p, unsigned long v) +{ + unsigned char *a; + + a = p; + a[0] = v>>24; + a[1] = v>>16; + a[2] = v>>8; + a[3] = v; +} + +void +hnputs(void *p, unsigned short v) +{ + unsigned char *a; + + a = p; + a[0] = v>>8; + a[1] = v; +} + +unsigned long +nhgetl(void *p) +{ + unsigned char *a; + a = p; + return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0); +} + +unsigned short +nhgets(void *p) +{ + unsigned char *a; + a = p; + return (a[0]<<8)|(a[1]<<0); +} + +#define CLASS(p) ((*(unsigned char*)(p))>>6) + +unsigned long +parseip(char *to, char *from) +{ + int i; + char *p; + + p = from; + memset(to, 0, 4); + for(i = 0; i < 4 && *p; i++){ + to[i] = strtoul(p, &p, 10); + if(*p != '.' && *p != 0){ + memset(to, 0, 4); + return 0; + } + if(*p == '.') + p++; + } + switch(CLASS(to)){ + case 0: /* class A - 1 byte 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 byte net */ + if(i == 3){ + to[3] = to[2]; + to[2] = 0; + } + break; + } + return nhgetl(to); +} + +void +csclose(Chan *c) +{ + free(c->aux); +} + +long +csread(Chan *c, void *a, long n, vlong offset) +{ + if(c->aux == nil) + return 0; + return readstr(offset, a, n, c->aux); +} + +static struct +{ + char *name; + uint num; +} tab[] = { + "cs", 1, + "echo", 7, + "discard", 9, + "systat", 11, + "daytime", 13, + "netstat", 15, + "chargen", 19, + "ftp-data", 20, + "ftp", 21, + "ssh", 22, + "telnet", 23, + "smtp", 25, + "time", 37, + "whois", 43, + "dns", 53, + "domain", 53, + "uucp", 64, + "gopher", 70, + "rje", 77, + "finger", 79, + "http", 80, + "link", 87, + "supdup", 95, + "hostnames", 101, + "iso-tsap", 102, + "x400", 103, + "x400-snd", 104, + "csnet-ns", 105, + "pop-2", 109, + "pop3", 110, + "portmap", 111, + "uucp-path", 117, + "nntp", 119, + "netbios", 139, + "imap4", 143, + "NeWS", 144, + "print-srv", 170, + "z39.50", 210, + "fsb", 400, + "sysmon", 401, + "proxy", 402, + "proxyd", 404, + "https", 443, + "cifs", 445, + "ssmtp", 465, + "rexec", 512, + "login", 513, + "shell", 514, + "printer", 515, + "courier", 530, + "cscan", 531, + "uucp", 540, + "snntp", 563, + "9fs", 564, + "whoami", 565, + "guard", 566, + "ticket", 567, + "dlsftp", 666, + "fmclient", 729, + "imaps", 993, + "pop3s", 995, + "ingreslock", 1524, + "pptp", 1723, + "nfs", 2049, + "webster", 2627, + "weather", 3000, + "secstore", 5356, + "Xdisplay", 6000, + "styx", 6666, + "mpeg", 6667, + "rstyx", 6668, + "infdb", 6669, + "infsigner", 6671, + "infcsigner", 6672, + "inflogin", 6673, + "bandt", 7330, + "face", 32000, + "dhashgate", 11978, + "exportfs", 17007, + "rexexec", 17009, + "ncpu", 17010, + "cpu", 17013, + "glenglenda1", 17020, + "glenglenda2", 17021, + "glenglenda3", 17022, + "glenglenda4", 17023, + "glenglenda5", 17024, + "glenglenda6", 17025, + "glenglenda7", 17026, + "glenglenda8", 17027, + "glenglenda9", 17028, + "glenglenda10", 17029, + "flyboy", 17032, + "dlsftp", 17033, + "venti", 17034, + "wiki", 17035, + "vica", 17036, + 0 +}; + +static int +lookupport(char *s) +{ + int i; + char buf[10], *p; + + i = strtol(s, &p, 0); + if(*s && *p == 0) + return i; + + i = so_getservbyname(s, "tcp", buf); + if(i != -1) + return atoi(buf); + for(i=0; tab[i].name; i++) + if(strcmp(s, tab[i].name) == 0) + return tab[i].num; + return 0; +} + +static ulong +lookuphost(char *s) +{ + char to[4]; + ulong ip; + + memset(to, 0, sizeof to); + parseip(to, s); + ip = nhgetl(to); + if(ip != 0) + return ip; + if((s = hostlookup(s)) == nil) + return 0; + parseip(to, s); + ip = nhgetl(to); + free(s); + return ip; +} + +long +cswrite(Chan *c, void *a, long n, vlong offset) +{ + char *f[4]; + char *s, *ns; + ulong ip; + int nf, port; + + s = malloc(n+1); + if(s == nil) + error(Enomem); + if(waserror()){ + free(s); + nexterror(); + } + memmove(s, a, n); + s[n] = 0; + nf = getfields(s, f, nelem(f), 0, "!"); + if(nf != 3) + error("can't translate"); + + port = lookupport(f[2]); + if(port <= 0) + error("no translation for port found"); + + ip = lookuphost(f[1]); + if(ip == 0) + error("no translation for host found"); + + ns = smprint("/net/%s/clone %I!%d", f[0], ip, port); + if(ns == nil) + error(Enomem); + free(c->aux); + c->aux = ns; + poperror(); + free(s); + return n; +} + +Dev ipdevtab = +{ + 'I', + "ip", + + devreset, + ipinit, + devshutdown, + ipattach, + ipwalk, + ipstat, + ipopen, + devcreate, + ipclose, + ipread, + devbread, + ipwrite, + devbwrite, + devremove, + devwstat, +}; + diff --git a/sys/src/cmd/unix/drawterm/kern/devip.h b/sys/src/cmd/unix/drawterm/kern/devip.h new file mode 100755 index 000000000..950ad398d --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/devip.h @@ -0,0 +1,19 @@ +enum +{ + S_TCP, + S_UDP +}; + +int so_socket(int type); +void so_connect(int, unsigned long, unsigned short); +void so_getsockname(int, unsigned long*, unsigned short*); +void so_bind(int, int, unsigned short); +void so_listen(int); +int so_send(int, void*, int, int); +int so_recv(int, void*, int, int); +int so_accept(int, unsigned long*, unsigned short*); +int so_getservbyname(char*, char*, char*); +int so_gethostbyname(char*, char**, int); + +char* hostlookup(char*); + diff --git a/sys/src/cmd/unix/drawterm/kern/devlfd.c b/sys/src/cmd/unix/drawterm/kern/devlfd.c new file mode 100755 index 000000000..7c61e0c1e --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/devlfd.c @@ -0,0 +1,126 @@ +#include "u.h" +#include <errno.h> +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#undef pread +#undef pwrite + +Chan* +lfdchan(int fd) +{ + Chan *c; + + c = newchan(); + c->type = devno('L', 0); + c->aux = (void*)(uintptr)fd; + c->name = newcname("fd"); + c->mode = ORDWR; + c->qid.type = 0; + c->qid.path = 0; + c->qid.vers = 0; + c->dev = 0; + c->offset = 0; + return c; +} + +int +lfdfd(int fd) +{ + return newfd(lfdchan(fd)); +} + +static Chan* +lfdattach(char *x) +{ + USED(x); + + error(Egreg); + return nil; +} + +static Walkqid* +lfdwalk(Chan *c, Chan *nc, char **name, int nname) +{ + USED(c); + USED(nc); + USED(name); + USED(nname); + + error(Egreg); + return nil; +} + +static int +lfdstat(Chan *c, uchar *dp, int n) +{ + USED(c); + USED(dp); + USED(n); + error(Egreg); + return -1; +} + +static Chan* +lfdopen(Chan *c, int omode) +{ + USED(c); + USED(omode); + + error(Egreg); + return nil; +} + +static void +lfdclose(Chan *c) +{ + close((int)(uintptr)c->aux); +} + +static long +lfdread(Chan *c, void *buf, long n, vlong off) +{ + USED(off); /* can't pread on pipes */ + n = read((int)(uintptr)c->aux, buf, n); + if(n < 0){ + iprint("error %d\n", errno); + oserror(); + } + return n; +} + +static long +lfdwrite(Chan *c, void *buf, long n, vlong off) +{ + USED(off); /* can't pread on pipes */ + + n = write((int)(uintptr)c->aux, buf, n); + if(n < 0){ + iprint("error %d\n", errno); + oserror(); + } + return n; +} + +Dev lfddevtab = { + 'L', + "lfd", + + devreset, + devinit, + devshutdown, + lfdattach, + lfdwalk, + lfdstat, + lfdopen, + devcreate, + lfdclose, + lfdread, + devbread, + lfdwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/sys/src/cmd/unix/drawterm/kern/devmnt.c b/sys/src/cmd/unix/drawterm/kern/devmnt.c new file mode 100755 index 000000000..9121bfdb4 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/devmnt.c @@ -0,0 +1,1216 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +/* + * References are managed as follows: + * The channel to the server - a network connection or pipe - has one + * reference for every Chan open on the server. The server channel has + * c->mux set to the Mnt used for muxing control to that server. Mnts + * have no reference count; they go away when c goes away. + * Each channel derived from the mount point has mchan set to c, + * and increfs/decrefs mchan to manage references on the server + * connection. + */ + +#define MAXRPC (IOHDRSZ+8192) + +struct Mntrpc +{ + Chan* c; /* Channel for whom we are working */ + Mntrpc* list; /* Free/pending list */ + Fcall request; /* Outgoing file system protocol message */ + Fcall reply; /* Incoming reply */ + Mnt* m; /* Mount device during rpc */ + Rendez r; /* Place to hang out */ + uchar* rpc; /* I/O Data buffer */ + uint rpclen; /* len of buffer */ + Block *b; /* reply blocks */ + char done; /* Rpc completed */ + uvlong stime; /* start time for mnt statistics */ + ulong reqlen; /* request length for mnt statistics */ + ulong replen; /* reply length for mnt statistics */ + Mntrpc* flushed; /* message this one flushes */ +}; + +enum +{ + TAGSHIFT = 5, /* ulong has to be 32 bits */ + TAGMASK = (1<<TAGSHIFT)-1, + NMASK = (64*1024)>>TAGSHIFT, +}; + +struct Mntalloc +{ + Lock lk; + Mnt* list; /* Mount devices in use */ + Mnt* mntfree; /* Free list */ + Mntrpc* rpcfree; + int nrpcfree; + int nrpcused; + ulong id; + ulong tagmask[NMASK]; +}mntalloc; + +void mattach(Mnt*, Chan*, char*); +Mnt* mntchk(Chan*); +void mntdirfix(uchar*, Chan*); +Mntrpc* mntflushalloc(Mntrpc*, ulong); +void mntflushfree(Mnt*, Mntrpc*); +void mntfree(Mntrpc*); +void mntgate(Mnt*); +void mntpntfree(Mnt*); +void mntqrm(Mnt*, Mntrpc*); +Mntrpc* mntralloc(Chan*, ulong); +long mntrdwr(int, Chan*, void*, long, vlong); +int mntrpcread(Mnt*, Mntrpc*); +void mountio(Mnt*, Mntrpc*); +void mountmux(Mnt*, Mntrpc*); +void mountrpc(Mnt*, Mntrpc*); +int rpcattn(void*); +Chan* mntchan(void); + +char Esbadstat[] = "invalid directory entry received from server"; +char Enoversion[] = "version not established for mount channel"; + + +void (*mntstats)(int, Chan*, uvlong, ulong); + +static void +mntreset(void) +{ + mntalloc.id = 1; + mntalloc.tagmask[0] = 1; /* don't allow 0 as a tag */ + mntalloc.tagmask[NMASK-1] = 0x80000000UL; /* don't allow NOTAG */ + fmtinstall('F', fcallfmt); + fmtinstall('D', dirfmt); +/* We can't install %M since eipfmt does and is used in the kernel [sape] */ + + cinit(); +} + +/* + * Version is not multiplexed: message sent only once per connection. + */ +long +mntversion(Chan *c, char *version, int msize, int returnlen) +{ + Fcall f; + uchar *msg; + Mnt *m; + char *v; + long k, l; + uvlong oo; + char buf[128]; + + qlock(&c->umqlock); /* make sure no one else does this until we've established ourselves */ + if(waserror()){ + qunlock(&c->umqlock); + nexterror(); + } + + /* defaults */ + if(msize == 0) + msize = MAXRPC; + if(msize > c->iounit && c->iounit != 0) + msize = c->iounit; + v = version; + if(v == nil || v[0] == '\0') + v = VERSION9P; + + /* validity */ + if(msize < 0) + error("bad iounit in version call"); + if(strncmp(v, VERSION9P, strlen(VERSION9P)) != 0) + error("bad 9P version specification"); + + m = c->mux; + + if(m != nil){ + qunlock(&c->umqlock); + poperror(); + + strecpy(buf, buf+sizeof buf, m->version); + k = strlen(buf); + if(strncmp(buf, v, k) != 0){ + snprint(buf, sizeof buf, "incompatible 9P versions %s %s", m->version, v); + error(buf); + } + if(returnlen > 0){ + if(returnlen < k) + error(Eshort); + memmove(version, buf, k); + } + return k; + } + + f.type = Tversion; + f.tag = NOTAG; + f.msize = msize; + f.version = v; + msg = malloc(8192+IOHDRSZ); + if(msg == nil) + exhausted("version memory"); + if(waserror()){ + free(msg); + nexterror(); + } + k = convS2M(&f, msg, 8192+IOHDRSZ); + if(k == 0) + error("bad fversion conversion on send"); + + lock(&c->ref.lk); + oo = c->offset; + c->offset += k; + unlock(&c->ref.lk); + + l = devtab[c->type]->write(c, msg, k, oo); + + if(l < k){ + lock(&c->ref.lk); + c->offset -= k - l; + unlock(&c->ref.lk); + error("short write in fversion"); + } + + /* message sent; receive and decode reply */ + k = devtab[c->type]->read(c, msg, 8192+IOHDRSZ, c->offset); + if(k <= 0) + error("EOF receiving fversion reply"); + + lock(&c->ref.lk); + c->offset += k; + unlock(&c->ref.lk); + + l = convM2S(msg, k, &f); + if(l != k) + error("bad fversion conversion on reply"); + if(f.type != Rversion){ + if(f.type == Rerror) + error(f.ename); + error("unexpected reply type in fversion"); + } + if(f.msize > msize) + error("server tries to increase msize in fversion"); + if(f.msize<256 || f.msize>1024*1024) + error("nonsense value of msize in fversion"); + k = strlen(f.version); + if(strncmp(f.version, v, k) != 0) + error("bad 9P version returned from server"); + + /* now build Mnt associated with this connection */ + lock(&mntalloc.lk); + m = mntalloc.mntfree; + if(m != 0) + mntalloc.mntfree = m->list; + else { + m = malloc(sizeof(Mnt)); + if(m == 0) { + unlock(&mntalloc.lk); + exhausted("mount devices"); + } + } + m->list = mntalloc.list; + mntalloc.list = m; + m->version = nil; + kstrdup(&m->version, f.version); + m->id = mntalloc.id++; + m->q = qopen(10*MAXRPC, 0, 0, nil); + m->msize = f.msize; + unlock(&mntalloc.lk); + + if(returnlen > 0){ + if(returnlen < k) + error(Eshort); + memmove(version, f.version, k); + } + + poperror(); /* msg */ + free(msg); + + lock(&m->lk); + m->queue = 0; + m->rip = 0; + + c->flag |= CMSG; + c->mux = m; + m->c = c; + unlock(&m->lk); + + poperror(); /* c */ + qunlock(&c->umqlock); + + return k; +} + +Chan* +mntauth(Chan *c, char *spec) +{ + Mnt *m; + Mntrpc *r; + + m = c->mux; + + if(m == nil){ + mntversion(c, VERSION9P, MAXRPC, 0); + m = c->mux; + if(m == nil) + error(Enoversion); + } + + c = mntchan(); + if(waserror()) { + /* Close must not be called since it will + * call mnt recursively + */ + chanfree(c); + nexterror(); + } + + r = mntralloc(0, m->msize); + + if(waserror()) { + mntfree(r); + nexterror(); + } + + r->request.type = Tauth; + r->request.afid = c->fid; + r->request.uname = up->user; + r->request.aname = spec; + mountrpc(m, r); + + c->qid = r->reply.aqid; + c->mchan = m->c; + incref(&m->c->ref); + c->mqid = c->qid; + c->mode = ORDWR; + + poperror(); /* r */ + mntfree(r); + + poperror(); /* c */ + + return c; + +} + +static Chan* +mntattach(char *muxattach) +{ + Mnt *m; + Chan *c; + Mntrpc *r; + struct bogus{ + Chan *chan; + Chan *authchan; + char *spec; + int flags; + }bogus; + + bogus = *((struct bogus *)muxattach); + c = bogus.chan; + + m = c->mux; + + if(m == nil){ + mntversion(c, nil, 0, 0); + m = c->mux; + if(m == nil) + error(Enoversion); + } + + c = mntchan(); + if(waserror()) { + /* Close must not be called since it will + * call mnt recursively + */ + chanfree(c); + nexterror(); + } + + r = mntralloc(0, m->msize); + + if(waserror()) { + mntfree(r); + nexterror(); + } + + r->request.type = Tattach; + r->request.fid = c->fid; + if(bogus.authchan == nil) + r->request.afid = NOFID; + else + r->request.afid = bogus.authchan->fid; + r->request.uname = up->user; + r->request.aname = bogus.spec; + mountrpc(m, r); + + c->qid = r->reply.qid; + c->mchan = m->c; + incref(&m->c->ref); + c->mqid = c->qid; + + poperror(); /* r */ + mntfree(r); + + poperror(); /* c */ + + if(bogus.flags&MCACHE) + c->flag |= CCACHE; + return c; +} + +Chan* +mntchan(void) +{ + Chan *c; + + c = devattach('M', 0); + lock(&mntalloc.lk); + c->dev = mntalloc.id++; + unlock(&mntalloc.lk); + + if(c->mchan) + panic("mntchan non-zero %p", c->mchan); + return c; +} + +static Walkqid* +mntwalk(Chan *c, Chan *nc, char **name, int nname) +{ + int i, alloc; + Mnt *m; + Mntrpc *r; + Walkqid *wq; + + if(nc != nil) + print("mntwalk: nc != nil\n"); + if(nname > MAXWELEM) + error("devmnt: too many name elements"); + alloc = 0; + wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); + if(waserror()){ + if(alloc && wq->clone!=nil) + cclose(wq->clone); + free(wq); + return nil; + } + + alloc = 0; + m = mntchk(c); + r = mntralloc(c, m->msize); + if(nc == nil){ + nc = devclone(c); + /* + * Until the other side accepts this fid, we can't mntclose it. + * Therefore set type to 0 for now; rootclose is known to be safe. + */ + nc->type = 0; + alloc = 1; + } + wq->clone = nc; + + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = Twalk; + r->request.fid = c->fid; + r->request.newfid = nc->fid; + r->request.nwname = nname; + memmove(r->request.wname, name, nname*sizeof(char*)); + + mountrpc(m, r); + + if(r->reply.nwqid > nname) + error("too many QIDs returned by walk"); + if(r->reply.nwqid < nname){ + if(alloc) + cclose(nc); + wq->clone = nil; + if(r->reply.nwqid == 0){ + free(wq); + wq = nil; + goto Return; + } + } + + /* move new fid onto mnt device and update its qid */ + if(wq->clone != nil){ + if(wq->clone != c){ + wq->clone->type = c->type; + wq->clone->mchan = c->mchan; + incref(&c->mchan->ref); + } + if(r->reply.nwqid > 0) + wq->clone->qid = r->reply.wqid[r->reply.nwqid-1]; + } + wq->nqid = r->reply.nwqid; + for(i=0; i<wq->nqid; i++) + wq->qid[i] = r->reply.wqid[i]; + + Return: + poperror(); + mntfree(r); + poperror(); + return wq; +} + +static int +mntstat(Chan *c, uchar *dp, int n) +{ + Mnt *m; + Mntrpc *r; + + if(n < BIT16SZ) + error(Eshortstat); + m = mntchk(c); + r = mntralloc(c, m->msize); + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = Tstat; + r->request.fid = c->fid; + mountrpc(m, r); + +/* r->reply.nstat is 16 bits + if(r->reply.nstat >= 1<<16) + error("returned stat buffer count too large"); +*/ + + if(r->reply.nstat > n){ + /* + * 12/31/2002 RSC + * + * This should be nstat-2, which is the first two + * bytes of the stat buffer. But dirstat and dirfstat + * depended on getting the full nstat (they didn't + * add BIT16SZ themselves). I fixed dirstat and dirfstat + * but am leaving this unchanged for now. After a + * few months, once enough of the relevant binaries + * have been recompiled for other reasons, we can + * change this to nstat-2. Devstat gets this right + * (via convD2M). + */ + /* doesn't fit; just patch the count and return */ + PBIT16((uchar*)dp, r->reply.nstat); + n = BIT16SZ; + }else{ + n = r->reply.nstat; + memmove(dp, r->reply.stat, n); + validstat(dp, n); + mntdirfix(dp, c); + } + poperror(); + mntfree(r); + return n; +} + +static Chan* +mntopencreate(int type, Chan *c, char *name, int omode, ulong perm) +{ + Mnt *m; + Mntrpc *r; + + m = mntchk(c); + r = mntralloc(c, m->msize); + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = type; + r->request.fid = c->fid; + r->request.mode = omode; + if(type == Tcreate){ + r->request.perm = perm; + r->request.name = name; + } + mountrpc(m, r); + + c->qid = r->reply.qid; + c->offset = 0; + c->mode = openmode(omode); + c->iounit = r->reply.iounit; + if(c->iounit == 0 || c->iounit > m->msize-IOHDRSZ) + c->iounit = m->msize-IOHDRSZ; + c->flag |= COPEN; + poperror(); + mntfree(r); + + if(c->flag & CCACHE) + copen(c); + + return c; +} + +static Chan* +mntopen(Chan *c, int omode) +{ + return mntopencreate(Topen, c, nil, omode, 0); +} + +static void +mntcreate(Chan *c, char *name, int omode, ulong perm) +{ + mntopencreate(Tcreate, c, name, omode, perm); +} + +static void +mntclunk(Chan *c, int t) +{ + Mnt *m; + Mntrpc *r; + + m = mntchk(c); + r = mntralloc(c, m->msize); + if(waserror()){ + mntfree(r); + nexterror(); + } + + r->request.type = t; + r->request.fid = c->fid; + mountrpc(m, r); + mntfree(r); + poperror(); +} + +void +muxclose(Mnt *m) +{ + Mntrpc *q, *r; + + for(q = m->queue; q; q = r) { + r = q->list; + mntfree(q); + } + m->id = 0; + free(m->version); + m->version = nil; + mntpntfree(m); +} + +void +mntpntfree(Mnt *m) +{ + Mnt *f, **l; + Queue *q; + + lock(&mntalloc.lk); + l = &mntalloc.list; + for(f = *l; f; f = f->list) { + if(f == m) { + *l = m->list; + break; + } + l = &f->list; + } + m->list = mntalloc.mntfree; + mntalloc.mntfree = m; + q = m->q; + unlock(&mntalloc.lk); + + qfree(q); +} + +static void +mntclose(Chan *c) +{ + mntclunk(c, Tclunk); +} + +static void +mntremove(Chan *c) +{ + mntclunk(c, Tremove); +} + +static int +mntwstat(Chan *c, uchar *dp, int n) +{ + Mnt *m; + Mntrpc *r; + + m = mntchk(c); + r = mntralloc(c, m->msize); + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = Twstat; + r->request.fid = c->fid; + r->request.nstat = n; + r->request.stat = dp; + mountrpc(m, r); + poperror(); + mntfree(r); + return n; +} + +static long +mntread(Chan *c, void *buf, long n, vlong off) +{ + uchar *p, *e; + int nc, cache, isdir, dirlen; + + isdir = 0; + cache = c->flag & CCACHE; + if(c->qid.type & QTDIR) { + cache = 0; + isdir = 1; + } + + p = buf; + if(cache) { + nc = cread(c, buf, n, off); + if(nc > 0) { + n -= nc; + if(n == 0) + return nc; + p += nc; + off += nc; + } + n = mntrdwr(Tread, c, p, n, off); + cupdate(c, p, n, off); + return n + nc; + } + + n = mntrdwr(Tread, c, buf, n, off); + if(isdir) { + for(e = &p[n]; p+BIT16SZ < e; p += dirlen){ + dirlen = BIT16SZ+GBIT16(p); + if(p+dirlen > e) + break; + validstat(p, dirlen); + mntdirfix(p, c); + } + if(p != e) + error(Esbadstat); + } + return n; +} + +static long +mntwrite(Chan *c, void *buf, long n, vlong off) +{ + return mntrdwr(Twrite, c, buf, n, off); +} + +long +mntrdwr(int type, Chan *c, void *buf, long n, vlong off) +{ + Mnt *m; + Mntrpc *r; + char *uba; + int cache; + ulong cnt, nr, nreq; + + m = mntchk(c); + uba = buf; + cnt = 0; + cache = c->flag & CCACHE; + if(c->qid.type & QTDIR) + cache = 0; + for(;;) { + r = mntralloc(c, m->msize); + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = type; + r->request.fid = c->fid; + r->request.offset = off; + r->request.data = uba; + nr = n; + if(nr > m->msize-IOHDRSZ) + nr = m->msize-IOHDRSZ; + r->request.count = nr; + mountrpc(m, r); + nreq = r->request.count; + nr = r->reply.count; + if(nr > nreq) + nr = nreq; + + if(type == Tread) + r->b = bl2mem((uchar*)uba, r->b, nr); + else if(cache) + cwrite(c, (uchar*)uba, nr, off); + + poperror(); + mntfree(r); + off += nr; + uba += nr; + cnt += nr; + n -= nr; + if(nr != nreq || n == 0) + break; + } + return cnt; +} + +void +mountrpc(Mnt *m, Mntrpc *r) +{ + char *sn, *cn; + int t; + + r->reply.tag = 0; + r->reply.type = Tmax; /* can't ever be a valid message type */ + + mountio(m, r); + + t = r->reply.type; + switch(t) { + case Rerror: + error(r->reply.ename); + case Rflush: + error(Eintr); + default: + if(t == r->request.type+1) + break; + sn = "?"; + if(m->c->name != nil) + sn = m->c->name->s; + cn = "?"; + if(r->c != nil && r->c->name != nil) + cn = r->c->name->s; + print("mnt: proc %lud: mismatch from %s %s rep 0x%lux tag %d fid %d T%d R%d rp %d\n", + up->pid, sn, cn, + r, r->request.tag, r->request.fid, r->request.type, + r->reply.type, r->reply.tag); + error(Emountrpc); + } +} + +void +mountio(Mnt *m, Mntrpc *r) +{ + int n; + + while(waserror()) { + if(m->rip == up) + mntgate(m); + if(strcmp(up->errstr, Eintr) != 0){ + mntflushfree(m, r); + nexterror(); + } + r = mntflushalloc(r, m->msize); + } + + lock(&m->lk); + r->m = m; + r->list = m->queue; + m->queue = r; + unlock(&m->lk); + + /* Transmit a file system rpc */ + if(m->msize == 0) + panic("msize"); + n = convS2M(&r->request, r->rpc, m->msize); + if(n < 0) + panic("bad message type in mountio"); + if(devtab[m->c->type]->write(m->c, r->rpc, n, 0) != n) + error(Emountrpc); + r->stime = fastticks(nil); + r->reqlen = n; + + /* Gate readers onto the mount point one at a time */ + for(;;) { + lock(&m->lk); + if(m->rip == 0) + break; + unlock(&m->lk); + sleep(&r->r, rpcattn, r); + if(r->done){ + poperror(); + mntflushfree(m, r); + return; + } + } + m->rip = up; + unlock(&m->lk); + while(r->done == 0) { + if(mntrpcread(m, r) < 0) + error(Emountrpc); + mountmux(m, r); + } + mntgate(m); + poperror(); + mntflushfree(m, r); +} + +static int +doread(Mnt *m, int len) +{ + Block *b; + + while(qlen(m->q) < len){ + b = devtab[m->c->type]->bread(m->c, m->msize, 0); + if(b == nil) + return -1; + if(BLEN(b) == 0){ + freeblist(b); + return -1; + } + qaddlist(m->q, b); + } + return 0; +} + +int +mntrpcread(Mnt *m, Mntrpc *r) +{ + int i, t, len, hlen; + Block *b, **l, *nb; + + r->reply.type = 0; + r->reply.tag = 0; + + /* read at least length, type, and tag and pullup to a single block */ + if(doread(m, BIT32SZ+BIT8SZ+BIT16SZ) < 0) + return -1; + nb = pullupqueue(m->q, BIT32SZ+BIT8SZ+BIT16SZ); + + /* read in the rest of the message, avoid rediculous (for now) message sizes */ + len = GBIT32(nb->rp); + if(len > m->msize){ + qdiscard(m->q, qlen(m->q)); + return -1; + } + if(doread(m, len) < 0) + return -1; + + /* pullup the header (i.e. everything except data) */ + t = nb->rp[BIT32SZ]; + switch(t){ + case Rread: + hlen = BIT32SZ+BIT8SZ+BIT16SZ+BIT32SZ; + break; + default: + hlen = len; + break; + } + nb = pullupqueue(m->q, hlen); + + if(convM2S(nb->rp, len, &r->reply) <= 0){ + /* bad message, dump it */ + print("mntrpcread: convM2S failed\n"); + qdiscard(m->q, len); + return -1; + } + + /* hang the data off of the fcall struct */ + l = &r->b; + *l = nil; + do { + b = qremove(m->q); + if(hlen > 0){ + b->rp += hlen; + len -= hlen; + hlen = 0; + } + i = BLEN(b); + if(i <= len){ + len -= i; + *l = b; + l = &(b->next); + } else { + /* split block and put unused bit back */ + nb = allocb(i-len); + memmove(nb->wp, b->rp+len, i-len); + b->wp = b->rp+len; + nb->wp += i-len; + qputback(m->q, nb); + *l = b; + return 0; + } + }while(len > 0); + + return 0; +} + +void +mntgate(Mnt *m) +{ + Mntrpc *q; + + lock(&m->lk); + m->rip = 0; + for(q = m->queue; q; q = q->list) { + if(q->done == 0) + if(wakeup(&q->r)) + break; + } + unlock(&m->lk); +} + +void +mountmux(Mnt *m, Mntrpc *r) +{ + Mntrpc **l, *q; + + lock(&m->lk); + l = &m->queue; + for(q = *l; q; q = q->list) { + /* look for a reply to a message */ + if(q->request.tag == r->reply.tag) { + *l = q->list; + if(q != r) { + /* + * Completed someone else. + * Trade pointers to receive buffer. + */ + q->reply = r->reply; + q->b = r->b; + r->b = nil; + } + q->done = 1; + unlock(&m->lk); + if(mntstats != 0) + (*mntstats)(q->request.type, + m->c, q->stime, + q->reqlen + r->replen); + if(q != r) + wakeup(&q->r); + return; + } + l = &q->list; + } + unlock(&m->lk); + print("unexpected reply tag %ud; type %d\n", r->reply.tag, r->reply.type); +} + +/* + * Create a new flush request and chain the previous + * requests from it + */ +Mntrpc* +mntflushalloc(Mntrpc *r, ulong iounit) +{ + Mntrpc *fr; + + fr = mntralloc(0, iounit); + + fr->request.type = Tflush; + if(r->request.type == Tflush) + fr->request.oldtag = r->request.oldtag; + else + fr->request.oldtag = r->request.tag; + fr->flushed = r; + + return fr; +} + +/* + * Free a chain of flushes. Remove each unanswered + * flush and the original message from the unanswered + * request queue. Mark the original message as done + * and if it hasn't been answered set the reply to to + * Rflush. + */ +void +mntflushfree(Mnt *m, Mntrpc *r) +{ + Mntrpc *fr; + + while(r){ + fr = r->flushed; + if(!r->done){ + r->reply.type = Rflush; + mntqrm(m, r); + } + if(fr) + mntfree(r); + r = fr; + } +} + +int +alloctag(void) +{ + int i, j; + ulong v; + + for(i = 0; i < NMASK; i++){ + v = mntalloc.tagmask[i]; + if(v == ~0) + continue; + for(j = 0; j < 1<<TAGSHIFT; j++) + if((v & (1<<j)) == 0){ + mntalloc.tagmask[i] |= 1<<j; + return (i<<TAGSHIFT) + j; + } + } + panic("no friggin tags left"); + return NOTAG; +} + +void +freetag(int t) +{ + mntalloc.tagmask[t>>TAGSHIFT] &= ~(1<<(t&TAGMASK)); +} + +Mntrpc* +mntralloc(Chan *c, ulong msize) +{ + Mntrpc *new; + + lock(&mntalloc.lk); + new = mntalloc.rpcfree; + if(new == nil){ + new = malloc(sizeof(Mntrpc)); + if(new == nil) { + unlock(&mntalloc.lk); + exhausted("mount rpc header"); + } + /* + * The header is split from the data buffer as + * mountmux may swap the buffer with another header. + */ + new->rpc = mallocz(msize, 0); + if(new->rpc == nil){ + free(new); + unlock(&mntalloc.lk); + exhausted("mount rpc buffer"); + } + new->rpclen = msize; + new->request.tag = alloctag(); + } + else { + mntalloc.rpcfree = new->list; + mntalloc.nrpcfree--; + if(new->rpclen < msize){ + free(new->rpc); + new->rpc = mallocz(msize, 0); + if(new->rpc == nil){ + free(new); + mntalloc.nrpcused--; + unlock(&mntalloc.lk); + exhausted("mount rpc buffer"); + } + new->rpclen = msize; + } + } + mntalloc.nrpcused++; + unlock(&mntalloc.lk); + new->c = c; + new->done = 0; + new->flushed = nil; + new->b = nil; + return new; +} + +void +mntfree(Mntrpc *r) +{ + if(r->b != nil) + freeblist(r->b); + lock(&mntalloc.lk); + if(mntalloc.nrpcfree >= 10){ + free(r->rpc); + free(r); + freetag(r->request.tag); + } + else{ + r->list = mntalloc.rpcfree; + mntalloc.rpcfree = r; + mntalloc.nrpcfree++; + } + mntalloc.nrpcused--; + unlock(&mntalloc.lk); +} + +void +mntqrm(Mnt *m, Mntrpc *r) +{ + Mntrpc **l, *f; + + lock(&m->lk); + r->done = 1; + + l = &m->queue; + for(f = *l; f; f = f->list) { + if(f == r) { + *l = r->list; + break; + } + l = &f->list; + } + unlock(&m->lk); +} + +Mnt* +mntchk(Chan *c) +{ + Mnt *m; + + /* This routine is mostly vestiges of prior lives; now it's just sanity checking */ + + if(c->mchan == nil) + panic("mntchk 1: nil mchan c %s\n", c2name(c)); + + m = c->mchan->mux; + + if(m == nil) + print("mntchk 2: nil mux c %s c->mchan %s \n", c2name(c), c2name(c->mchan)); + + /* + * Was it closed and reused (was error(Eshutdown); now, it can't happen) + */ + if(m->id == 0 || m->id >= c->dev) + panic("mntchk 3: can't happen"); + + return m; +} + +/* + * Rewrite channel type and dev for in-flight data to + * reflect local values. These entries are known to be + * the first two in the Dir encoding after the count. + */ +void +mntdirfix(uchar *dirbuf, Chan *c) +{ + uint r; + + r = devtab[c->type]->dc; + dirbuf += BIT16SZ; /* skip count */ + PBIT16(dirbuf, r); + dirbuf += BIT16SZ; + PBIT32(dirbuf, c->dev); +} + +int +rpcattn(void *v) +{ + Mntrpc *r; + + r = v; + return r->done || r->m->rip == 0; +} + +Dev mntdevtab = { + 'M', + "mnt", + + mntreset, + devinit, + devshutdown, + mntattach, + mntwalk, + mntstat, + mntopen, + mntcreate, + mntclose, + mntread, + devbread, + mntwrite, + devbwrite, + mntremove, + mntwstat, +}; diff --git a/sys/src/cmd/unix/drawterm/kern/devmouse.c b/sys/src/cmd/unix/drawterm/kern/devmouse.c new file mode 100755 index 000000000..3ee0c8b6b --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/devmouse.c @@ -0,0 +1,237 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include "draw.h" +#include "memdraw.h" +#include "screen.h" + +int mousequeue = 1; + +Mouseinfo mouse; +Cursorinfo cursor; + +static int mousechanged(void*); + +enum{ + Qdir, + Qcursor, + Qmouse +}; + +Dirtab mousedir[]={ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "cursor", {Qcursor}, 0, 0666, + "mouse", {Qmouse}, 0, 0666, +}; + +#define NMOUSE (sizeof(mousedir)/sizeof(Dirtab)) + +static Chan* +mouseattach(char *spec) +{ + return devattach('m', spec); +} + +static Walkqid* +mousewalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, mousedir, NMOUSE, devgen); +} + +static int +mousestat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, mousedir, NMOUSE, devgen); +} + +static Chan* +mouseopen(Chan *c, int omode) +{ + switch((long)c->qid.path){ + case Qdir: + if(omode != OREAD) + error(Eperm); + break; + case Qmouse: + lock(&mouse.lk); + if(mouse.open){ + unlock(&mouse.lk); + error(Einuse); + } + mouse.open = 1; + unlock(&mouse.lk); + break; + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +void +mouseclose(Chan *c) +{ + if(!(c->flag&COPEN)) + return; + + switch((long)c->qid.path) { + case Qmouse: + lock(&mouse.lk); + mouse.open = 0; + unlock(&mouse.lk); + cursorarrow(); + } +} + + +long +mouseread(Chan *c, void *va, long n, vlong offset) +{ + char buf[4*12+1]; + uchar *p; + int i, nn; + ulong msec; +/* static int map[8] = {0, 4, 2, 6, 1, 5, 3, 7 }; */ + + p = va; + switch((long)c->qid.path){ + case Qdir: + return devdirread(c, va, n, mousedir, NMOUSE, devgen); + + case Qcursor: + if(offset != 0) + return 0; + if(n < 2*4+2*2*16) + error(Eshort); + n = 2*4+2*2*16; + lock(&cursor.lk); + BPLONG(p+0, cursor.offset.x); + BPLONG(p+4, cursor.offset.y); + memmove(p+8, cursor.clr, 2*16); + memmove(p+40, cursor.set, 2*16); + unlock(&cursor.lk); + return n; + + case Qmouse: + while(mousechanged(0) == 0) + sleep(&mouse.r, mousechanged, 0); + + lock(&screen.lk); + if(screen.reshaped) { + screen.reshaped = 0; + sprint(buf, "t%11d %11d", 0, ticks()); + if(n > 1+2*12) + n = 1+2*12; + memmove(va, buf, n); + unlock(&screen.lk); + return n; + } + unlock(&screen.lk); + + lock(&mouse.lk); + i = mouse.ri; + nn = (mouse.wi + Mousequeue - i) % Mousequeue; + if(nn < 1) + panic("empty mouse queue"); + msec = ticks(); + while(nn > 1) { + if(mouse.queue[i].msec + Mousewindow > msec) + break; + i = (i+1)%Mousequeue; + nn--; + } + sprint(buf, "m%11d %11d %11d %11d", + mouse.queue[i].xy.x, + mouse.queue[i].xy.y, + mouse.queue[i].buttons, + mouse.queue[i].msec); + mouse.ri = (i+1)%Mousequeue; + unlock(&mouse.lk); + if(n > 1+4*12) + n = 1+4*12; + memmove(va, buf, n); + return n; + } + return 0; +} + +long +mousewrite(Chan *c, void *va, long n, vlong offset) +{ + char *p; + Point pt; + char buf[64]; + + USED(offset); + + p = va; + switch((long)c->qid.path){ + case Qdir: + error(Eisdir); + + case Qcursor: + if(n < 2*4+2*2*16){ + cursorarrow(); + }else{ + n = 2*4+2*2*16; + lock(&cursor.lk); + cursor.offset.x = BGLONG(p+0); + cursor.offset.y = BGLONG(p+4); + memmove(cursor.clr, p+8, 2*16); + memmove(cursor.set, p+40, 2*16); + unlock(&cursor.lk); + setcursor(); + } + return n; + + case Qmouse: + if(n > sizeof buf-1) + n = sizeof buf -1; + memmove(buf, va, n); + buf[n] = 0; + p = 0; + pt.x = strtoul(buf+1, &p, 0); + if(p == 0) + error(Eshort); + pt.y = strtoul(p, 0, 0); + if(ptinrect(pt, gscreen->r)) + mouseset(pt); + return n; + } + + error(Egreg); + return -1; +} + +int +mousechanged(void *a) +{ + USED(a); + + return mouse.ri != mouse.wi || screen.reshaped; +} + +Dev mousedevtab = { + 'm', + "mouse", + + devreset, + devinit, + devshutdown, + mouseattach, + mousewalk, + mousestat, + mouseopen, + devcreate, + mouseclose, + mouseread, + devbread, + mousewrite, + devbwrite, + devremove, + devwstat, +}; + diff --git a/sys/src/cmd/unix/drawterm/kern/devpipe.c b/sys/src/cmd/unix/drawterm/kern/devpipe.c new file mode 100755 index 000000000..73401f99f --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/devpipe.c @@ -0,0 +1,398 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include "netif.h" + +typedef struct Pipe Pipe; +struct Pipe +{ + QLock lk; + Pipe *next; + int ref; + ulong path; + Queue *q[2]; + int qref[2]; +}; + +struct +{ + Lock lk; + ulong path; +} pipealloc; + +enum +{ + Qdir, + Qdata0, + Qdata1, +}; + +Dirtab pipedir[] = +{ + ".", {Qdir,0,QTDIR}, 0, DMDIR|0500, + "data", {Qdata0}, 0, 0600, + "data1", {Qdata1}, 0, 0600, +}; +#define NPIPEDIR 3 + +static void +pipeinit(void) +{ + if(conf.pipeqsize == 0){ + if(conf.nmach > 1) + conf.pipeqsize = 256*1024; + else + conf.pipeqsize = 32*1024; + } +} + +/* + * create a pipe, no streams are created until an open + */ +static Chan* +pipeattach(char *spec) +{ + Pipe *p; + Chan *c; + + c = devattach('|', spec); + p = malloc(sizeof(Pipe)); + if(p == 0) + exhausted("memory"); + p->ref = 1; + + p->q[0] = qopen(conf.pipeqsize, 0, 0, 0); + if(p->q[0] == 0){ + free(p); + exhausted("memory"); + } + p->q[1] = qopen(conf.pipeqsize, 0, 0, 0); + if(p->q[1] == 0){ + free(p->q[0]); + free(p); + exhausted("memory"); + } + + lock(&pipealloc.lk); + p->path = ++pipealloc.path; + unlock(&pipealloc.lk); + + mkqid(&c->qid, NETQID(2*p->path, Qdir), 0, QTDIR); + c->aux = p; + c->dev = 0; + return c; +} + +static int +pipegen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp) +{ + Qid q; + int len; + Pipe *p; + + USED(name); + + if(i == DEVDOTDOT){ + devdir(c, c->qid, "#|", 0, eve, DMDIR|0555, dp); + return 1; + } + i++; /* skip . */ + if(tab==0 || i>=ntab) + return -1; + + tab += i; + p = c->aux; + switch((ulong)tab->qid.path){ + case Qdata0: + len = qlen(p->q[0]); + break; + case Qdata1: + len = qlen(p->q[1]); + break; + default: + len = tab->length; + break; + } + mkqid(&q, NETQID(NETID(c->qid.path), tab->qid.path), 0, QTFILE); + devdir(c, q, tab->name, len, eve, tab->perm, dp); + return 1; +} + + +static Walkqid* +pipewalk(Chan *c, Chan *nc, char **name, int nname) +{ + Walkqid *wq; + Pipe *p; + + wq = devwalk(c, nc, name, nname, pipedir, NPIPEDIR, pipegen); + if(wq != nil && wq->clone != nil && wq->clone != c){ + p = c->aux; + qlock(&p->lk); + p->ref++; + if(c->flag & COPEN){ + print("channel open in pipewalk\n"); + switch(NETTYPE(c->qid.path)){ + case Qdata0: + p->qref[0]++; + break; + case Qdata1: + p->qref[1]++; + break; + } + } + qunlock(&p->lk); + } + return wq; +} + +static int +pipestat(Chan *c, uchar *db, int n) +{ + Pipe *p; + Dir dir; + + p = c->aux; + + switch(NETTYPE(c->qid.path)){ + case Qdir: + devdir(c, c->qid, ".", 0, eve, DMDIR|0555, &dir); + break; + case Qdata0: + devdir(c, c->qid, "data", qlen(p->q[0]), eve, 0600, &dir); + break; + case Qdata1: + devdir(c, c->qid, "data1", qlen(p->q[1]), eve, 0600, &dir); + break; + default: + panic("pipestat"); + } + n = convD2M(&dir, db, n); + if(n < BIT16SZ) + error(Eshortstat); + return n; +} + +/* + * if the stream doesn't exist, create it + */ +static Chan* +pipeopen(Chan *c, int omode) +{ + Pipe *p; + + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Ebadarg); + c->mode = omode; + c->flag |= COPEN; + c->offset = 0; + return c; + } + + p = c->aux; + qlock(&p->lk); + switch(NETTYPE(c->qid.path)){ + case Qdata0: + p->qref[0]++; + break; + case Qdata1: + p->qref[1]++; + break; + } + qunlock(&p->lk); + + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + c->iounit = qiomaxatomic; + return c; +} + +static void +pipeclose(Chan *c) +{ + Pipe *p; + + p = c->aux; + qlock(&p->lk); + + if(c->flag & COPEN){ + /* + * closing either side hangs up the stream + */ + switch(NETTYPE(c->qid.path)){ + case Qdata0: + p->qref[0]--; + if(p->qref[0] == 0){ + qhangup(p->q[1], 0); + qclose(p->q[0]); + } + break; + case Qdata1: + p->qref[1]--; + if(p->qref[1] == 0){ + qhangup(p->q[0], 0); + qclose(p->q[1]); + } + break; + } + } + + + /* + * if both sides are closed, they are reusable + */ + if(p->qref[0] == 0 && p->qref[1] == 0){ + qreopen(p->q[0]); + qreopen(p->q[1]); + } + + /* + * free the structure on last close + */ + p->ref--; + if(p->ref == 0){ + qunlock(&p->lk); + free(p->q[0]); + free(p->q[1]); + free(p); + } else + qunlock(&p->lk); +} + +static long +piperead(Chan *c, void *va, long n, vlong offset) +{ + Pipe *p; + + USED(offset); + + p = c->aux; + + switch(NETTYPE(c->qid.path)){ + case Qdir: + return devdirread(c, va, n, pipedir, NPIPEDIR, pipegen); + case Qdata0: + return qread(p->q[0], va, n); + case Qdata1: + return qread(p->q[1], va, n); + default: + panic("piperead"); + } + return -1; /* not reached */ +} + +static Block* +pipebread(Chan *c, long n, ulong offset) +{ + Pipe *p; + + p = c->aux; + + switch(NETTYPE(c->qid.path)){ + case Qdata0: + return qbread(p->q[0], n); + case Qdata1: + return qbread(p->q[1], n); + } + + return devbread(c, n, offset); +} + +/* + * a write to a closed pipe causes a note to be sent to + * the process. + */ +static long +pipewrite(Chan *c, void *va, long n, vlong offset) +{ + Pipe *p; + + USED(offset); + if(!islo()) + print("pipewrite hi %lux\n", getcallerpc(&c)); + + if(waserror()) { + /* avoid notes when pipe is a mounted queue */ + if((c->flag & CMSG) == 0) + postnote(up, 1, "sys: write on closed pipe", NUser); + nexterror(); + } + + p = c->aux; + + switch(NETTYPE(c->qid.path)){ + case Qdata0: + n = qwrite(p->q[1], va, n); + break; + + case Qdata1: + n = qwrite(p->q[0], va, n); + break; + + default: + panic("pipewrite"); + } + + poperror(); + return n; +} + +static long +pipebwrite(Chan *c, Block *bp, ulong offset) +{ + long n; + Pipe *p; + + USED(offset); + + if(waserror()) { + /* avoid notes when pipe is a mounted queue */ + if((c->flag & CMSG) == 0) + postnote(up, 1, "sys: write on closed pipe", NUser); + nexterror(); + } + + p = c->aux; + switch(NETTYPE(c->qid.path)){ + case Qdata0: + n = qbwrite(p->q[1], bp); + break; + + case Qdata1: + n = qbwrite(p->q[0], bp); + break; + + default: + n = 0; + panic("pipebwrite"); + } + + poperror(); + return n; +} + +Dev pipedevtab = { + '|', + "pipe", + + devreset, + pipeinit, + devshutdown, + pipeattach, + pipewalk, + pipestat, + pipeopen, + devcreate, + pipeclose, + piperead, + pipebread, + pipewrite, + pipebwrite, + devremove, + devwstat, +}; diff --git a/sys/src/cmd/unix/drawterm/kern/devroot.c b/sys/src/cmd/unix/drawterm/kern/devroot.c new file mode 100755 index 000000000..db081ff1f --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/devroot.c @@ -0,0 +1,299 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +enum +{ + Qdir = 0, + Qboot = 0x1000, + Qmnt = 0x2000, + Qfactotum, + + Nrootfiles = 32, + Nbootfiles = 32, + Nmntfiles = 2, +}; + +typedef struct Dirlist Dirlist; +struct Dirlist +{ + uint base; + Dirtab *dir; + uchar **data; + int ndir; + int mdir; +}; + +static Dirtab rootdir[Nrootfiles] = { + "#/", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "boot", {Qboot, 0, QTDIR}, 0, DMDIR|0555, + "mnt", {Qmnt, 0, QTDIR}, 0, DMDIR|0555, +}; +static uchar *rootdata[Nrootfiles]; +static Dirlist rootlist = +{ + 0, + rootdir, + rootdata, + 3, + Nrootfiles +}; + +static Dirtab bootdir[Nbootfiles] = { + "boot", {Qboot, 0, QTDIR}, 0, DMDIR|0555, +}; +static uchar *bootdata[Nbootfiles]; +static Dirlist bootlist = +{ + Qboot, + bootdir, + bootdata, + 1, + Nbootfiles +}; + +static uchar *mntdata[Nmntfiles]; +static Dirtab mntdir[Nmntfiles] = { + "mnt", {Qmnt, 0, QTDIR}, 0, DMDIR|0555, + "factotum", {Qfactotum, 0, QTDIR}, 0, DMDIR|0555, +}; +static Dirlist mntlist = +{ + Qmnt, + mntdir, + mntdata, + 2, + Nmntfiles +}; + +/* + * add a file to the list + */ +static void +addlist(Dirlist *l, char *name, uchar *contents, ulong len, int perm) +{ + Dirtab *d; + + if(l->ndir >= l->mdir) + panic("too many root files"); + l->data[l->ndir] = contents; + d = &l->dir[l->ndir]; + strcpy(d->name, name); + d->length = len; + d->perm = perm; + d->qid.type = 0; + d->qid.vers = 0; + d->qid.path = ++l->ndir + l->base; + if(perm & DMDIR) + d->qid.type |= QTDIR; +} + +/* + * add a root file + */ +void +addbootfile(char *name, uchar *contents, ulong len) +{ + addlist(&bootlist, name, contents, len, 0555); +} + +/* + * add a root directory + */ +static void +addrootdir(char *name) +{ + addlist(&rootlist, name, nil, 0, DMDIR|0555); +} + +static void +rootreset(void) +{ + addrootdir("bin"); + addrootdir("dev"); + addrootdir("env"); + addrootdir("fd"); + addrootdir("net"); + addrootdir("net.alt"); + addrootdir("proc"); + addrootdir("root"); + addrootdir("srv"); +} + +static Chan* +rootattach(char *spec) +{ + return devattach('/', spec); +} + +static int +rootgen(Chan *c, char *name, Dirtab *dirt, int ndirt, int s, Dir *dp) +{ + int t; + Dirtab *d; + Dirlist *l; + + USED(dirt); + USED(ndirt); + + switch((int)c->qid.path){ + case Qdir: + if(s == DEVDOTDOT){ + Qid tqiddir = {Qdir, 0, QTDIR}; + devdir(c, tqiddir, "#/", 0, eve, 0555, dp); + return 1; + } + return devgen(c, name, rootlist.dir, rootlist.ndir, s, dp); + case Qmnt: + if(s == DEVDOTDOT){ + Qid tqiddir = {Qdir, 0, QTDIR}; + devdir(c, tqiddir, "#/", 0, eve, 0555, dp); + return 1; + } + return devgen(c, name, mntlist.dir, mntlist.ndir, s, dp); + case Qboot: + if(s == DEVDOTDOT){ + Qid tqiddir = {Qdir, 0, QTDIR}; + devdir(c, tqiddir, "#/", 0, eve, 0555, dp); + return 1; + } + return devgen(c, name, bootlist.dir, bootlist.ndir, s, dp); + default: + if(s == DEVDOTDOT){ + Qid tqiddir = {Qdir, 0, QTDIR}; + tqiddir.path = c->qid.path&0xF000; + devdir(c, tqiddir, "#/", 0, eve, 0555, dp); + return 1; + } + if(s != 0) + return -1; + switch((int)c->qid.path & 0xF000){ + case Qdir: + t = c->qid.path-1; + l = &rootlist; + break; + case Qboot: + t = c->qid.path - Qboot - 1; + l = &bootlist; + break; + case Qmnt: + t = c->qid.path - Qmnt - 1; + l = &mntlist; + break; + default: + return -1; + } + if(t >= l->ndir) + return -1; +if(t < 0){ +print("rootgen %llud %d %d\n", c->qid.path, s, t); +panic("whoops"); +} + d = &l->dir[t]; + devdir(c, d->qid, d->name, d->length, eve, d->perm, dp); + return 1; + } + return -1; +} + +static Walkqid* +rootwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, rootgen); +} + +static int +rootstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, nil, 0, rootgen); +} + +static Chan* +rootopen(Chan *c, int omode) +{ + return devopen(c, omode, nil, 0, devgen); +} + +/* + * sysremove() knows this is a nop + */ +static void +rootclose(Chan *c) +{ + USED(c); +} + +static long +rootread(Chan *c, void *buf, long n, vlong off) +{ + ulong t; + Dirtab *d; + Dirlist *l; + uchar *data; + ulong offset = off; + + t = c->qid.path; + switch(t){ + case Qdir: + case Qboot: + case Qmnt: + return devdirread(c, buf, n, nil, 0, rootgen); + } + + if(t&Qboot) + l = &bootlist; + else if(t&Qmnt) + l = &mntlist; + else + l = &bootlist; + t &= 0xFFF; + t--; + + if(t >= l->ndir) + error(Egreg); + + d = &l->dir[t]; + data = l->data[t]; + if(offset >= d->length) + return 0; + if(offset+n > d->length) + n = d->length - offset; + memmove(buf, data+offset, n); + return n; +} + +static long +rootwrite(Chan *c, void *v, long n, vlong o) +{ + USED(c); + USED(v); + USED(n); + USED(o); + + error(Egreg); + return 0; +} + +Dev rootdevtab = { + '/', + "root", + + rootreset, + devinit, + devshutdown, + rootattach, + rootwalk, + rootstat, + rootopen, + devcreate, + rootclose, + rootread, + devbread, + rootwrite, + devbwrite, + devremove, + devwstat, +}; + diff --git a/sys/src/cmd/unix/drawterm/kern/devssl.c b/sys/src/cmd/unix/drawterm/kern/devssl.c new file mode 100755 index 000000000..3ad021f9f --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/devssl.c @@ -0,0 +1,1517 @@ +/* + * devssl - secure sockets layer + */ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include "libsec.h" + +#define NOSPOOKS 1 + +typedef struct OneWay OneWay; +struct OneWay +{ + QLock q; + QLock ctlq; + + void *state; /* encryption state */ + int slen; /* hash data length */ + uchar *secret; /* secret */ + ulong mid; /* message id */ +}; + +enum +{ + /* connection states */ + Sincomplete= 0, + Sclear= 1, + Sencrypting= 2, + Sdigesting= 4, + Sdigenc= Sencrypting|Sdigesting, + + /* encryption algorithms */ + Noencryption= 0, + DESCBC= 1, + DESECB= 2, + RC4= 3 +}; + +typedef struct Dstate Dstate; +struct Dstate +{ + Chan *c; /* io channel */ + uchar state; /* state of connection */ + int ref; /* serialized by dslock for atomic destroy */ + + uchar encryptalg; /* encryption algorithm */ + ushort blocklen; /* blocking length */ + + ushort diglen; /* length of digest */ + DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*); /* hash func */ + + /* for SSL format */ + int max; /* maximum unpadded data per msg */ + int maxpad; /* maximum padded data per msg */ + + /* input side */ + OneWay in; + Block *processed; + Block *unprocessed; + + /* output side */ + OneWay out; + + /* protections */ + char *user; + int perm; +}; + +enum +{ + Maxdmsg= 1<<16, + Maxdstate= 128, /* must be a power of 2 */ +}; + +Lock dslock; +int dshiwat; +char *dsname[Maxdstate]; +Dstate *dstate[Maxdstate]; +char *encalgs; +char *hashalgs; + +enum{ + Qtopdir = 1, /* top level directory */ + Qprotodir, + Qclonus, + Qconvdir, /* directory for a conversation */ + Qdata, + Qctl, + Qsecretin, + Qsecretout, + Qencalgs, + Qhashalgs, +}; + +#define TYPE(x) ((x).path & 0xf) +#define CONV(x) (((x).path >> 5)&(Maxdstate-1)) +#define QID(c, y) (((c)<<5) | (y)) + +static void ensure(Dstate*, Block**, int); +static void consume(Block**, uchar*, int); +static void setsecret(OneWay*, uchar*, int); +static Block* encryptb(Dstate*, Block*, int); +static Block* decryptb(Dstate*, Block*); +static Block* digestb(Dstate*, Block*, int); +static void checkdigestb(Dstate*, Block*); +static Chan* buftochan(char*); +static void sslhangup(Dstate*); +static Dstate* dsclone(Chan *c); +static void dsnew(Chan *c, Dstate **); +static long sslput(Dstate *s, Block * volatile b); + +char *sslnames[] = { + /* unused */ 0, + /* topdir */ 0, + /* protodir */ 0, + "clone", + /* convdir */ 0, + "data", + "ctl", + "secretin", + "secretout", + "encalgs", + "hashalgs", +}; + +static int +sslgen(Chan *c, char *n, Dirtab *d, int nd, int s, Dir *dp) +{ + Qid q; + Dstate *ds; + char name[16], *p, *nm; + int ft; + + USED(n); + USED(nd); + USED(d); + + q.type = QTFILE; + q.vers = 0; + + ft = TYPE(c->qid); + switch(ft) { + case Qtopdir: + if(s == DEVDOTDOT){ + q.path = QID(0, Qtopdir); + q.type = QTDIR; + devdir(c, q, "#D", 0, eve, 0555, dp); + return 1; + } + if(s > 0) + return -1; + q.path = QID(0, Qprotodir); + q.type = QTDIR; + devdir(c, q, "ssl", 0, eve, 0555, dp); + return 1; + case Qprotodir: + if(s == DEVDOTDOT){ + q.path = QID(0, Qtopdir); + q.type = QTDIR; + devdir(c, q, ".", 0, eve, 0555, dp); + return 1; + } + if(s < dshiwat) { + q.path = QID(s, Qconvdir); + q.type = QTDIR; + ds = dstate[s]; + if(ds != 0) + nm = ds->user; + else + nm = eve; + if(dsname[s] == nil){ + sprint(name, "%d", s); + kstrdup(&dsname[s], name); + } + devdir(c, q, dsname[s], 0, nm, 0555, dp); + return 1; + } + if(s > dshiwat) + return -1; + q.path = QID(0, Qclonus); + devdir(c, q, "clone", 0, eve, 0555, dp); + return 1; + case Qconvdir: + if(s == DEVDOTDOT){ + q.path = QID(0, Qprotodir); + q.type = QTDIR; + devdir(c, q, "ssl", 0, eve, 0555, dp); + return 1; + } + ds = dstate[CONV(c->qid)]; + if(ds != 0) + nm = ds->user; + else + nm = eve; + switch(s) { + default: + return -1; + case 0: + q.path = QID(CONV(c->qid), Qctl); + p = "ctl"; + break; + case 1: + q.path = QID(CONV(c->qid), Qdata); + p = "data"; + break; + case 2: + q.path = QID(CONV(c->qid), Qsecretin); + p = "secretin"; + break; + case 3: + q.path = QID(CONV(c->qid), Qsecretout); + p = "secretout"; + break; + case 4: + q.path = QID(CONV(c->qid), Qencalgs); + p = "encalgs"; + break; + case 5: + q.path = QID(CONV(c->qid), Qhashalgs); + p = "hashalgs"; + break; + } + devdir(c, q, p, 0, nm, 0660, dp); + return 1; + case Qclonus: + devdir(c, c->qid, sslnames[TYPE(c->qid)], 0, eve, 0555, dp); + return 1; + default: + ds = dstate[CONV(c->qid)]; + if(ds != 0) + nm = ds->user; + else + nm = eve; + devdir(c, c->qid, sslnames[TYPE(c->qid)], 0, nm, 0660, dp); + return 1; + } + return -1; +} + +static Chan* +sslattach(char *spec) +{ + Chan *c; + + c = devattach('D', spec); + c->qid.path = QID(0, Qtopdir); + c->qid.vers = 0; + c->qid.type = QTDIR; + return c; +} + +static Walkqid* +sslwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, sslgen); +} + +static int +sslstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, nil, 0, sslgen); +} + +static Chan* +sslopen(Chan *c, int omode) +{ + Dstate *s, **pp; + int perm; + int ft; + + perm = 0; + omode &= 3; + switch(omode) { + case OREAD: + perm = 4; + break; + case OWRITE: + perm = 2; + break; + case ORDWR: + perm = 6; + break; + } + + ft = TYPE(c->qid); + switch(ft) { + default: + panic("sslopen"); + case Qtopdir: + case Qprotodir: + case Qconvdir: + if(omode != OREAD) + error(Eperm); + break; + case Qclonus: + s = dsclone(c); + if(s == 0) + error(Enodev); + break; + case Qctl: + case Qdata: + case Qsecretin: + case Qsecretout: + if(waserror()) { + unlock(&dslock); + nexterror(); + } + lock(&dslock); + pp = &dstate[CONV(c->qid)]; + s = *pp; + if(s == 0) + dsnew(c, pp); + else { + if((perm & (s->perm>>6)) != perm + && (strcmp(up->user, s->user) != 0 + || (perm & s->perm) != perm)) + error(Eperm); + + s->ref++; + } + unlock(&dslock); + poperror(); + break; + case Qencalgs: + case Qhashalgs: + if(omode != OREAD) + error(Eperm); + break; + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static int +sslwstat(Chan *c, uchar *db, int n) +{ + Dir *dir; + Dstate *s; + int m; + + s = dstate[CONV(c->qid)]; + if(s == 0) + error(Ebadusefd); + if(strcmp(s->user, up->user) != 0) + error(Eperm); + + dir = smalloc(sizeof(Dir)+n); + m = convM2D(db, n, &dir[0], (char*)&dir[1]); + if(m == 0){ + free(dir); + error(Eshortstat); + } + + if(!emptystr(dir->uid)) + kstrdup(&s->user, dir->uid); + if(dir->mode != ~0) + s->perm = dir->mode; + + free(dir); + return m; +} + +static void +sslclose(Chan *c) +{ + Dstate *s; + int ft; + + ft = TYPE(c->qid); + switch(ft) { + case Qctl: + case Qdata: + case Qsecretin: + case Qsecretout: + if((c->flag & COPEN) == 0) + break; + + s = dstate[CONV(c->qid)]; + if(s == 0) + break; + + lock(&dslock); + if(--s->ref > 0) { + unlock(&dslock); + break; + } + dstate[CONV(c->qid)] = 0; + unlock(&dslock); + + if(s->user != nil) + free(s->user); + sslhangup(s); + if(s->c) + cclose(s->c); + if(s->in.secret) + free(s->in.secret); + if(s->out.secret) + free(s->out.secret); + if(s->in.state) + free(s->in.state); + if(s->out.state) + free(s->out.state); + free(s); + + } +} + +/* + * make sure we have at least 'n' bytes in list 'l' + */ +static void +ensure(Dstate *s, Block **l, int n) +{ + int sofar, i; + Block *b, *bl; + + sofar = 0; + for(b = *l; b; b = b->next){ + sofar += BLEN(b); + if(sofar >= n) + return; + l = &b->next; + } + + while(sofar < n){ + bl = devtab[s->c->type]->bread(s->c, Maxdmsg, 0); + if(bl == 0) + nexterror(); + *l = bl; + i = 0; + for(b = bl; b; b = b->next){ + i += BLEN(b); + l = &b->next; + } + if(i == 0) + error(Ehungup); + sofar += i; + } +} + +/* + * copy 'n' bytes from 'l' into 'p' and free + * the bytes in 'l' + */ +static void +consume(Block **l, uchar *p, int n) +{ + Block *b; + int i; + + for(; *l && n > 0; n -= i){ + b = *l; + i = BLEN(b); + if(i > n) + i = n; + memmove(p, b->rp, i); + b->rp += i; + p += i; + if(BLEN(b) < 0) + panic("consume"); + if(BLEN(b)) + break; + *l = b->next; + freeb(b); + } +} + +/* + * give back n bytes +static void +regurgitate(Dstate *s, uchar *p, int n) +{ + Block *b; + + if(n <= 0) + return; + b = s->unprocessed; + if(s->unprocessed == nil || b->rp - b->base < n) { + b = allocb(n); + memmove(b->wp, p, n); + b->wp += n; + b->next = s->unprocessed; + s->unprocessed = b; + } else { + b->rp -= n; + memmove(b->rp, p, n); + } +} + */ + +/* + * remove at most n bytes from the queue, if discard is set + * dump the remainder + */ +static Block* +qtake(Block **l, int n, int discard) +{ + Block *nb, *b, *first; + int i; + + first = *l; + for(b = first; b; b = b->next){ + i = BLEN(b); + if(i == n){ + if(discard){ + freeblist(b->next); + *l = 0; + } else + *l = b->next; + b->next = 0; + return first; + } else if(i > n){ + i -= n; + if(discard){ + freeblist(b->next); + b->wp -= i; + *l = 0; + } else { + nb = allocb(i); + memmove(nb->wp, b->rp+n, i); + nb->wp += i; + b->wp -= i; + nb->next = b->next; + *l = nb; + } + b->next = 0; + if(BLEN(b) < 0) + panic("qtake"); + return first; + } else + n -= i; + if(BLEN(b) < 0) + panic("qtake"); + } + *l = 0; + return first; +} + +/* + * We can't let Eintr's lose data since the program + * doing the read may be able to handle it. The only + * places Eintr is possible is during the read's in consume. + * Therefore, we make sure we can always put back the bytes + * consumed before the last ensure. + */ +static Block* +sslbread(Chan *c, long n, ulong o) +{ + Dstate * volatile s; + Block *b; + uchar consumed[3], *p; + int toconsume; + int len, pad; + + USED(o); + s = dstate[CONV(c->qid)]; + if(s == 0) + panic("sslbread"); + if(s->state == Sincomplete) + error(Ebadusefd); + + qlock(&s->in.q); + if(waserror()){ + qunlock(&s->in.q); + nexterror(); + } + + if(s->processed == 0){ + /* + * Read in the whole message. Until we've got it all, + * it stays on s->unprocessed, so that if we get Eintr, + * we'll pick up where we left off. + */ + ensure(s, &s->unprocessed, 3); + s->unprocessed = pullupblock(s->unprocessed, 2); + p = s->unprocessed->rp; + if(p[0] & 0x80){ + len = ((p[0] & 0x7f)<<8) | p[1]; + ensure(s, &s->unprocessed, len); + pad = 0; + toconsume = 2; + } else { + s->unprocessed = pullupblock(s->unprocessed, 3); + len = ((p[0] & 0x3f)<<8) | p[1]; + pad = p[2]; + if(pad > len){ + print("pad %d buf len %d\n", pad, len); + error("bad pad in ssl message"); + } + toconsume = 3; + } + ensure(s, &s->unprocessed, toconsume+len); + + /* skip header */ + consume(&s->unprocessed, consumed, toconsume); + + /* grab the next message and decode/decrypt it */ + b = qtake(&s->unprocessed, len, 0); + + if(blocklen(b) != len) + print("devssl: sslbread got wrong count %d != %d", blocklen(b), len); + + if(waserror()){ + qunlock(&s->in.ctlq); + if(b != nil) + freeb(b); + nexterror(); + } + qlock(&s->in.ctlq); + switch(s->state){ + case Sencrypting: + if(b == nil) + error("ssl message too short (encrypting)"); + b = decryptb(s, b); + break; + case Sdigesting: + b = pullupblock(b, s->diglen); + if(b == nil) + error("ssl message too short (digesting)"); + checkdigestb(s, b); + pullblock(&b, s->diglen); + len -= s->diglen; + break; + case Sdigenc: + b = decryptb(s, b); + b = pullupblock(b, s->diglen); + if(b == nil) + error("ssl message too short (dig+enc)"); + checkdigestb(s, b); + pullblock(&b, s->diglen); + len -= s->diglen; + break; + } + + /* remove pad */ + if(pad) + s->processed = qtake(&b, len - pad, 1); + else + s->processed = b; + b = nil; + s->in.mid++; + qunlock(&s->in.ctlq); + poperror(); + } + + /* return at most what was asked for */ + b = qtake(&s->processed, n, 0); + + qunlock(&s->in.q); + poperror(); + + return b; +} + +static long +sslread(Chan *c, void *a, long n, vlong off) +{ + Block * volatile b; + Block *nb; + uchar *va; + int i; + char buf[128]; + ulong offset = off; + int ft; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, sslgen); + + ft = TYPE(c->qid); + switch(ft) { + default: + error(Ebadusefd); + case Qctl: + ft = CONV(c->qid); + sprint(buf, "%d", ft); + return readstr(offset, a, n, buf); + case Qdata: + b = sslbread(c, n, offset); + break; + case Qencalgs: + return readstr(offset, a, n, encalgs); + break; + case Qhashalgs: + return readstr(offset, a, n, hashalgs); + break; + } + + if(waserror()){ + freeblist(b); + nexterror(); + } + + n = 0; + va = a; + for(nb = b; nb; nb = nb->next){ + i = BLEN(nb); + memmove(va+n, nb->rp, i); + n += i; + } + + freeblist(b); + poperror(); + + return n; +} + +/* + * this algorithm doesn't have to be great since we're just + * trying to obscure the block fill + */ +static void +randfill(uchar *buf, int len) +{ + while(len-- > 0) + *buf++ = fastrand(); +} + +static long +sslbwrite(Chan *c, Block *b, ulong o) +{ + Dstate * volatile s; + long rv; + + USED(o); + s = dstate[CONV(c->qid)]; + if(s == nil) + panic("sslbwrite"); + + if(s->state == Sincomplete){ + freeb(b); + error(Ebadusefd); + } + + /* lock so split writes won't interleave */ + if(waserror()){ + qunlock(&s->out.q); + nexterror(); + } + qlock(&s->out.q); + + rv = sslput(s, b); + + poperror(); + qunlock(&s->out.q); + + return rv; +} + +/* + * use SSL record format, add in count, digest and/or encrypt. + * the write is interruptable. if it is interrupted, we'll + * get out of sync with the far side. not much we can do about + * it since we don't know if any bytes have been written. + */ +static long +sslput(Dstate *s, Block * volatile b) +{ + Block *nb; + int h, n, m, pad, rv; + uchar *p; + int offset; + + if(waserror()){ +iprint("error: %s\n", up->errstr); + if(b != nil) + free(b); + nexterror(); + } + + rv = 0; + while(b != nil){ + m = n = BLEN(b); + h = s->diglen + 2; + + /* trim to maximum block size */ + pad = 0; + if(m > s->max){ + m = s->max; + } else if(s->blocklen != 1){ + pad = (m + s->diglen)%s->blocklen; + if(pad){ + if(m > s->maxpad){ + pad = 0; + m = s->maxpad; + } else { + pad = s->blocklen - pad; + h++; + } + } + } + + rv += m; + if(m != n){ + nb = allocb(m + h + pad); + memmove(nb->wp + h, b->rp, m); + nb->wp += m + h; + b->rp += m; + } else { + /* add header space */ + nb = padblock(b, h); + b = 0; + } + m += s->diglen; + + /* SSL style count */ + if(pad){ + nb = padblock(nb, -pad); + randfill(nb->wp, pad); + nb->wp += pad; + m += pad; + + p = nb->rp; + p[0] = (m>>8); + p[1] = m; + p[2] = pad; + offset = 3; + } else { + p = nb->rp; + p[0] = (m>>8) | 0x80; + p[1] = m; + offset = 2; + } + + switch(s->state){ + case Sencrypting: + nb = encryptb(s, nb, offset); + break; + case Sdigesting: + nb = digestb(s, nb, offset); + break; + case Sdigenc: + nb = digestb(s, nb, offset); + nb = encryptb(s, nb, offset); + break; + } + + s->out.mid++; + + m = BLEN(nb); + devtab[s->c->type]->bwrite(s->c, nb, s->c->offset); + s->c->offset += m; + } + + poperror(); + return rv; +} + +static void +setsecret(OneWay *w, uchar *secret, int n) +{ + if(w->secret) + free(w->secret); + + w->secret = smalloc(n); + memmove(w->secret, secret, n); + w->slen = n; +} + +static void +initDESkey(OneWay *w) +{ + if(w->state){ + free(w->state); + w->state = 0; + } + + w->state = smalloc(sizeof(DESstate)); + if(w->slen >= 16) + setupDESstate(w->state, w->secret, w->secret+8); + else if(w->slen >= 8) + setupDESstate(w->state, w->secret, 0); + else + error("secret too short"); +} + +/* + * 40 bit DES is the same as 56 bit DES. However, + * 16 bits of the key are masked to zero. + */ +static void +initDESkey_40(OneWay *w) +{ + uchar key[8]; + + if(w->state){ + free(w->state); + w->state = 0; + } + + if(w->slen >= 8){ + memmove(key, w->secret, 8); + key[0] &= 0x0f; + key[2] &= 0x0f; + key[4] &= 0x0f; + key[6] &= 0x0f; + } + + w->state = malloc(sizeof(DESstate)); + if(w->slen >= 16) + setupDESstate(w->state, key, w->secret+8); + else if(w->slen >= 8) + setupDESstate(w->state, key, 0); + else + error("secret too short"); +} + +static void +initRC4key(OneWay *w) +{ + if(w->state){ + free(w->state); + w->state = 0; + } + + w->state = smalloc(sizeof(RC4state)); + setupRC4state(w->state, w->secret, w->slen); +} + +/* + * 40 bit RC4 is the same as n-bit RC4. However, + * we ignore all but the first 40 bits of the key. + */ +static void +initRC4key_40(OneWay *w) +{ + if(w->state){ + free(w->state); + w->state = 0; + } + + if(w->slen > 5) + w->slen = 5; + + w->state = malloc(sizeof(RC4state)); + setupRC4state(w->state, w->secret, w->slen); +} + +/* + * 128 bit RC4 is the same as n-bit RC4. However, + * we ignore all but the first 128 bits of the key. + */ +static void +initRC4key_128(OneWay *w) +{ + if(w->state){ + free(w->state); + w->state = 0; + } + + if(w->slen > 16) + w->slen = 16; + + w->state = malloc(sizeof(RC4state)); + setupRC4state(w->state, w->secret, w->slen); +} + + +typedef struct Hashalg Hashalg; +struct Hashalg +{ + char *name; + int diglen; + DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*); +}; + +Hashalg hashtab[] = +{ + { "md4", MD4dlen, md4, }, + { "md5", MD5dlen, md5, }, + { "sha1", SHA1dlen, sha1, }, + { "sha", SHA1dlen, sha1, }, + { 0 } +}; + +static int +parsehashalg(char *p, Dstate *s) +{ + Hashalg *ha; + + for(ha = hashtab; ha->name; ha++){ + if(strcmp(p, ha->name) == 0){ + s->hf = ha->hf; + s->diglen = ha->diglen; + s->state &= ~Sclear; + s->state |= Sdigesting; + return 0; + } + } + return -1; +} + +typedef struct Encalg Encalg; +struct Encalg +{ + char *name; + int blocklen; + int alg; + void (*keyinit)(OneWay*); +}; + +#ifdef NOSPOOKS +Encalg encrypttab[] = +{ + { "descbc", 8, DESCBC, initDESkey, }, /* DEPRECATED -- use des_56_cbc */ + { "desecb", 8, DESECB, initDESkey, }, /* DEPRECATED -- use des_56_ecb */ + { "des_56_cbc", 8, DESCBC, initDESkey, }, + { "des_56_ecb", 8, DESECB, initDESkey, }, + { "des_40_cbc", 8, DESCBC, initDESkey_40, }, + { "des_40_ecb", 8, DESECB, initDESkey_40, }, + { "rc4", 1, RC4, initRC4key_40, }, /* DEPRECATED -- use rc4_X */ + { "rc4_256", 1, RC4, initRC4key, }, + { "rc4_128", 1, RC4, initRC4key_128, }, + { "rc4_40", 1, RC4, initRC4key_40, }, + { 0 } +}; +#else +Encalg encrypttab[] = +{ + { "des_40_cbc", 8, DESCBC, initDESkey_40, }, + { "des_40_ecb", 8, DESECB, initDESkey_40, }, + { "rc4", 1, RC4, initRC4key_40, }, /* DEPRECATED -- use rc4_X */ + { "rc4_40", 1, RC4, initRC4key_40, }, + { 0 } +}; +#endif /* NOSPOOKS */ + +static int +parseencryptalg(char *p, Dstate *s) +{ + Encalg *ea; + + for(ea = encrypttab; ea->name; ea++){ + if(strcmp(p, ea->name) == 0){ + s->encryptalg = ea->alg; + s->blocklen = ea->blocklen; + (*ea->keyinit)(&s->in); + (*ea->keyinit)(&s->out); + s->state &= ~Sclear; + s->state |= Sencrypting; + return 0; + } + } + return -1; +} + +static long +sslwrite(Chan *c, void *a, long n, vlong o) +{ + Dstate * volatile s; + Block * volatile b; + int m, t; + char *p, *np, *e, buf[128]; + uchar *x; + + USED(o); + s = dstate[CONV(c->qid)]; + if(s == 0) + panic("sslwrite"); + + t = TYPE(c->qid); + if(t == Qdata){ + if(s->state == Sincomplete) + error(Ebadusefd); + + /* lock should a write gets split over multiple records */ + if(waserror()){ + qunlock(&s->out.q); + nexterror(); + } + qlock(&s->out.q); + p = a; +if(0) iprint("write %d %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux\n", + n, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); + e = p + n; + do { + m = e - p; + if(m > s->max) + m = s->max; + + b = allocb(m); + if(waserror()){ + freeb(b); + nexterror(); + } + memmove(b->wp, p, m); + poperror(); + b->wp += m; + + sslput(s, b); + + p += m; + } while(p < e); + p = a; +if(0) iprint("wrote %d %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux\n", + n, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); + poperror(); + qunlock(&s->out.q); + return n; + } + + /* mutex with operations using what we're about to change */ + if(waserror()){ + qunlock(&s->in.ctlq); + qunlock(&s->out.q); + nexterror(); + } + qlock(&s->in.ctlq); + qlock(&s->out.q); + + switch(t){ + default: + panic("sslwrite"); + case Qsecretin: + setsecret(&s->in, a, n); + goto out; + case Qsecretout: + setsecret(&s->out, a, n); + goto out; + case Qctl: + break; + } + + if(n >= sizeof(buf)) + error("arg too long"); + strncpy(buf, a, n); + buf[n] = 0; + p = strchr(buf, '\n'); + if(p) + *p = 0; + p = strchr(buf, ' '); + if(p) + *p++ = 0; + + if(strcmp(buf, "fd") == 0){ + s->c = buftochan(p); + + /* default is clear (msg delimiters only) */ + s->state = Sclear; + s->blocklen = 1; + s->diglen = 0; + s->maxpad = s->max = (1<<15) - s->diglen - 1; + s->in.mid = 0; + s->out.mid = 0; + } else if(strcmp(buf, "alg") == 0 && p != 0){ + s->blocklen = 1; + s->diglen = 0; + + if(s->c == 0) + error("must set fd before algorithm"); + + s->state = Sclear; + s->maxpad = s->max = (1<<15) - s->diglen - 1; + if(strcmp(p, "clear") == 0){ + goto out; + } + + if(s->in.secret && s->out.secret == 0) + setsecret(&s->out, s->in.secret, s->in.slen); + if(s->out.secret && s->in.secret == 0) + setsecret(&s->in, s->out.secret, s->out.slen); + if(s->in.secret == 0 || s->out.secret == 0) + error("algorithm but no secret"); + + s->hf = 0; + s->encryptalg = Noencryption; + s->blocklen = 1; + + for(;;){ + np = strchr(p, ' '); + if(np) + *np++ = 0; + + if(parsehashalg(p, s) < 0) + if(parseencryptalg(p, s) < 0) + error("bad algorithm"); + + if(np == 0) + break; + p = np; + } + + if(s->hf == 0 && s->encryptalg == Noencryption) + error("bad algorithm"); + + if(s->blocklen != 1){ + s->max = (1<<15) - s->diglen - 1; + s->max -= s->max % s->blocklen; + s->maxpad = (1<<14) - s->diglen - 1; + s->maxpad -= s->maxpad % s->blocklen; + } else + s->maxpad = s->max = (1<<15) - s->diglen - 1; + } else if(strcmp(buf, "secretin") == 0 && p != 0) { + m = (strlen(p)*3)/2; + x = smalloc(m); + t = dec64(x, m, p, strlen(p)); + setsecret(&s->in, x, t); + free(x); + } else if(strcmp(buf, "secretout") == 0 && p != 0) { + m = (strlen(p)*3)/2 + 1; + x = smalloc(m); + t = dec64(x, m, p, strlen(p)); + setsecret(&s->out, x, t); + free(x); + } else + error(Ebadarg); + +out: + qunlock(&s->in.ctlq); + qunlock(&s->out.q); + poperror(); + return n; +} + +static void +sslinit(void) +{ + struct Encalg *e; + struct Hashalg *h; + int n; + char *cp; + + n = 1; + for(e = encrypttab; e->name != nil; e++) + n += strlen(e->name) + 1; + cp = encalgs = smalloc(n); + for(e = encrypttab;;){ + strcpy(cp, e->name); + cp += strlen(e->name); + e++; + if(e->name == nil) + break; + *cp++ = ' '; + } + *cp = 0; + + n = 1; + for(h = hashtab; h->name != nil; h++) + n += strlen(h->name) + 1; + cp = hashalgs = smalloc(n); + for(h = hashtab;;){ + strcpy(cp, h->name); + cp += strlen(h->name); + h++; + if(h->name == nil) + break; + *cp++ = ' '; + } + *cp = 0; +} + +Dev ssldevtab = { + 'D', + "ssl", + + devreset, + sslinit, + devshutdown, + sslattach, + sslwalk, + sslstat, + sslopen, + devcreate, + sslclose, + sslread, + sslbread, + sslwrite, + sslbwrite, + devremove, + sslwstat, +}; + +static Block* +encryptb(Dstate *s, Block *b, int offset) +{ + uchar *p, *ep, *p2, *ip, *eip; + DESstate *ds; + + switch(s->encryptalg){ + case DESECB: + ds = s->out.state; + ep = b->rp + BLEN(b); + for(p = b->rp + offset; p < ep; p += 8) + block_cipher(ds->expanded, p, 0); + break; + case DESCBC: + ds = s->out.state; + ep = b->rp + BLEN(b); + for(p = b->rp + offset; p < ep; p += 8){ + p2 = p; + ip = ds->ivec; + for(eip = ip+8; ip < eip; ) + *p2++ ^= *ip++; + block_cipher(ds->expanded, p, 0); + memmove(ds->ivec, p, 8); + } + break; + case RC4: + rc4(s->out.state, b->rp + offset, BLEN(b) - offset); + break; + } + return b; +} + +static Block* +decryptb(Dstate *s, Block *bin) +{ + Block *b, **l; + uchar *p, *ep, *tp, *ip, *eip; + DESstate *ds; + uchar tmp[8]; + int i; + + l = &bin; + for(b = bin; b; b = b->next){ + /* make sure we have a multiple of s->blocklen */ + if(s->blocklen > 1){ + i = BLEN(b); + if(i % s->blocklen){ + *l = b = pullupblock(b, i + s->blocklen - (i%s->blocklen)); + if(b == 0) + error("ssl encrypted message too short"); + } + } + l = &b->next; + + /* decrypt */ + switch(s->encryptalg){ + case DESECB: + ds = s->in.state; + ep = b->rp + BLEN(b); + for(p = b->rp; p < ep; p += 8) + block_cipher(ds->expanded, p, 1); + break; + case DESCBC: + ds = s->in.state; + ep = b->rp + BLEN(b); + for(p = b->rp; p < ep;){ + memmove(tmp, p, 8); + block_cipher(ds->expanded, p, 1); + tp = tmp; + ip = ds->ivec; + for(eip = ip+8; ip < eip; ){ + *p++ ^= *ip; + *ip++ = *tp++; + } + } + break; + case RC4: + rc4(s->in.state, b->rp, BLEN(b)); + break; + } + } + return bin; +} + +static Block* +digestb(Dstate *s, Block *b, int offset) +{ + uchar *p; + DigestState ss; + uchar msgid[4]; + ulong n, h; + OneWay *w; + + w = &s->out; + + memset(&ss, 0, sizeof(ss)); + h = s->diglen + offset; + n = BLEN(b) - h; + + /* hash secret + message */ + (*s->hf)(w->secret, w->slen, 0, &ss); + (*s->hf)(b->rp + h, n, 0, &ss); + + /* hash message id */ + p = msgid; + n = w->mid; + *p++ = n>>24; + *p++ = n>>16; + *p++ = n>>8; + *p = n; + (*s->hf)(msgid, 4, b->rp + offset, &ss); + + return b; +} + +static void +checkdigestb(Dstate *s, Block *bin) +{ + uchar *p; + DigestState ss; + uchar msgid[4]; + int n, h; + OneWay *w; + uchar digest[128]; + Block *b; + + w = &s->in; + + memset(&ss, 0, sizeof(ss)); + + /* hash secret */ + (*s->hf)(w->secret, w->slen, 0, &ss); + + /* hash message */ + h = s->diglen; + for(b = bin; b; b = b->next){ + n = BLEN(b) - h; + if(n < 0) + panic("checkdigestb"); + (*s->hf)(b->rp + h, n, 0, &ss); + h = 0; + } + + /* hash message id */ + p = msgid; + n = w->mid; + *p++ = n>>24; + *p++ = n>>16; + *p++ = n>>8; + *p = n; + (*s->hf)(msgid, 4, digest, &ss); + + if(memcmp(digest, bin->rp, s->diglen) != 0) + error("bad digest"); +} + +/* get channel associated with an fd */ +static Chan* +buftochan(char *p) +{ + Chan *c; + int fd; + + if(p == 0) + error(Ebadarg); + fd = strtoul(p, 0, 0); + if(fd < 0) + error(Ebadarg); + c = fdtochan(fd, -1, 0, 1); /* error check and inc ref */ + if(devtab[c->type] == &ssldevtab){ + cclose(c); + error("cannot ssl encrypt devssl files"); + } + return c; +} + +/* hand up a digest connection */ +static void +sslhangup(Dstate *s) +{ + Block *b; + + qlock(&s->in.q); + for(b = s->processed; b; b = s->processed){ + s->processed = b->next; + freeb(b); + } + if(s->unprocessed){ + freeb(s->unprocessed); + s->unprocessed = 0; + } + s->state = Sincomplete; + qunlock(&s->in.q); +} + +static Dstate* +dsclone(Chan *ch) +{ + int i; + Dstate *ret; + + if(waserror()) { + unlock(&dslock); + nexterror(); + } + lock(&dslock); + ret = nil; + for(i=0; i<Maxdstate; i++){ + if(dstate[i] == nil){ + dsnew(ch, &dstate[i]); + ret = dstate[i]; + break; + } + } + unlock(&dslock); + poperror(); + return ret; +} + +static void +dsnew(Chan *ch, Dstate **pp) +{ + Dstate *s; + int t; + + *pp = s = malloc(sizeof(*s)); + if(!s) + error(Enomem); + if(pp - dstate >= dshiwat) + dshiwat++; + memset(s, 0, sizeof(*s)); + s->state = Sincomplete; + s->ref = 1; + kstrdup(&s->user, up->user); + s->perm = 0660; + t = TYPE(ch->qid); + if(t == Qclonus) + t = Qctl; + ch->qid.path = QID(pp - dstate, t); + ch->qid.vers = 0; +} diff --git a/sys/src/cmd/unix/drawterm/kern/devtab.c b/sys/src/cmd/unix/drawterm/kern/devtab.c new file mode 100755 index 000000000..e16a188fd --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/devtab.c @@ -0,0 +1,35 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +extern Dev consdevtab; +extern Dev rootdevtab; +extern Dev pipedevtab; +extern Dev ssldevtab; +extern Dev tlsdevtab; +extern Dev mousedevtab; +extern Dev drawdevtab; +extern Dev ipdevtab; +extern Dev fsdevtab; +extern Dev mntdevtab; +extern Dev lfddevtab; +extern Dev audiodevtab; + +Dev *devtab[] = { + &rootdevtab, + &consdevtab, + &pipedevtab, + &ssldevtab, + &tlsdevtab, + &mousedevtab, + &drawdevtab, + &ipdevtab, + &fsdevtab, + &mntdevtab, + &lfddevtab, + &audiodevtab, + 0 +}; + diff --git a/sys/src/cmd/unix/drawterm/kern/devtls.c b/sys/src/cmd/unix/drawterm/kern/devtls.c new file mode 100755 index 000000000..6f439a241 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/devtls.c @@ -0,0 +1,2185 @@ +/* + * devtls - record layer for transport layer security 1.0 and secure sockets layer 3.0 + */ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include "libsec.h" + +typedef struct OneWay OneWay; +typedef struct Secret Secret; +typedef struct TlsRec TlsRec; +typedef struct TlsErrs TlsErrs; + +enum { + Statlen= 1024, /* max. length of status or stats message */ + /* buffer limits */ + MaxRecLen = 1<<14, /* max payload length of a record layer message */ + MaxCipherRecLen = MaxRecLen + 2048, + RecHdrLen = 5, + MaxMacLen = SHA1dlen, + + /* protocol versions we can accept */ + TLSVersion = 0x0301, + SSL3Version = 0x0300, + ProtocolVersion = 0x0301, /* maximum version we speak */ + MinProtoVersion = 0x0300, /* limits on version we accept */ + MaxProtoVersion = 0x03ff, + + /* connection states */ + SHandshake = 1 << 0, /* doing handshake */ + SOpen = 1 << 1, /* application data can be sent */ + SRClose = 1 << 2, /* remote side has closed down */ + SLClose = 1 << 3, /* sent a close notify alert */ + SAlert = 1 << 5, /* sending or sent a fatal alert */ + SError = 1 << 6, /* some sort of error has occured */ + SClosed = 1 << 7, /* it is all over */ + + /* record types */ + RChangeCipherSpec = 20, + RAlert, + RHandshake, + RApplication, + + SSL2ClientHello = 1, + HSSL2ClientHello = 9, /* local convention; see tlshand.c */ + + /* alerts */ + ECloseNotify = 0, + EUnexpectedMessage = 10, + EBadRecordMac = 20, + EDecryptionFailed = 21, + ERecordOverflow = 22, + EDecompressionFailure = 30, + EHandshakeFailure = 40, + ENoCertificate = 41, + EBadCertificate = 42, + EUnsupportedCertificate = 43, + ECertificateRevoked = 44, + ECertificateExpired = 45, + ECertificateUnknown = 46, + EIllegalParameter = 47, + EUnknownCa = 48, + EAccessDenied = 49, + EDecodeError = 50, + EDecryptError = 51, + EExportRestriction = 60, + EProtocolVersion = 70, + EInsufficientSecurity = 71, + EInternalError = 80, + EUserCanceled = 90, + ENoRenegotiation = 100, + + EMAX = 256 +}; + +struct Secret +{ + char *encalg; /* name of encryption alg */ + char *hashalg; /* name of hash alg */ + int (*enc)(Secret*, uchar*, int); + int (*dec)(Secret*, uchar*, int); + int (*unpad)(uchar*, int, int); + DigestState *(*mac)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*); + int block; /* encryption block len, 0 if none */ + int maclen; + void *enckey; + uchar mackey[MaxMacLen]; +}; + +struct OneWay +{ + QLock io; /* locks io access */ + QLock seclock; /* locks secret paramaters */ + ulong seq; + Secret *sec; /* cipher in use */ + Secret *new; /* cipher waiting for enable */ +}; + +struct TlsRec +{ + Chan *c; /* io channel */ + int ref; /* serialized by tdlock for atomic destroy */ + int version; /* version of the protocol we are speaking */ + char verset; /* version has been set */ + char opened; /* opened command every issued? */ + char err[ERRMAX]; /* error message to return to handshake requests */ + vlong handin; /* bytes communicated by the record layer */ + vlong handout; + vlong datain; + vlong dataout; + + Lock statelk; + int state; + int debug; + + /* record layer mac functions for different protocol versions */ + void (*packMac)(Secret*, uchar*, uchar*, uchar*, uchar*, int, uchar*); + + /* input side -- protected by in.io */ + OneWay in; + Block *processed; /* next bunch of application data */ + Block *unprocessed; /* data read from c but not parsed into records */ + + /* handshake queue */ + Lock hqlock; /* protects hqref, alloc & free of handq, hprocessed */ + int hqref; + Queue *handq; /* queue of handshake messages */ + Block *hprocessed; /* remainder of last block read from handq */ + QLock hqread; /* protects reads for hprocessed, handq */ + + /* output side */ + OneWay out; + + /* protections */ + char *user; + int perm; +}; + +struct TlsErrs{ + int err; + int sslerr; + int tlserr; + int fatal; + char *msg; +}; + +static TlsErrs tlserrs[] = { + {ECloseNotify, ECloseNotify, ECloseNotify, 0, "close notify"}, + {EUnexpectedMessage, EUnexpectedMessage, EUnexpectedMessage, 1, "unexpected message"}, + {EBadRecordMac, EBadRecordMac, EBadRecordMac, 1, "bad record mac"}, + {EDecryptionFailed, EIllegalParameter, EDecryptionFailed, 1, "decryption failed"}, + {ERecordOverflow, EIllegalParameter, ERecordOverflow, 1, "record too long"}, + {EDecompressionFailure, EDecompressionFailure, EDecompressionFailure, 1, "decompression failed"}, + {EHandshakeFailure, EHandshakeFailure, EHandshakeFailure, 1, "could not negotiate acceptable security parameters"}, + {ENoCertificate, ENoCertificate, ECertificateUnknown, 1, "no appropriate certificate available"}, + {EBadCertificate, EBadCertificate, EBadCertificate, 1, "corrupted or invalid certificate"}, + {EUnsupportedCertificate, EUnsupportedCertificate, EUnsupportedCertificate, 1, "unsupported certificate type"}, + {ECertificateRevoked, ECertificateRevoked, ECertificateRevoked, 1, "revoked certificate"}, + {ECertificateExpired, ECertificateExpired, ECertificateExpired, 1, "expired certificate"}, + {ECertificateUnknown, ECertificateUnknown, ECertificateUnknown, 1, "unacceptable certificate"}, + {EIllegalParameter, EIllegalParameter, EIllegalParameter, 1, "illegal parameter"}, + {EUnknownCa, EHandshakeFailure, EUnknownCa, 1, "unknown certificate authority"}, + {EAccessDenied, EHandshakeFailure, EAccessDenied, 1, "access denied"}, + {EDecodeError, EIllegalParameter, EDecodeError, 1, "error decoding message"}, + {EDecryptError, EIllegalParameter, EDecryptError, 1, "error decrypting message"}, + {EExportRestriction, EHandshakeFailure, EExportRestriction, 1, "export restriction violated"}, + {EProtocolVersion, EIllegalParameter, EProtocolVersion, 1, "protocol version not supported"}, + {EInsufficientSecurity, EHandshakeFailure, EInsufficientSecurity, 1, "stronger security routines required"}, + {EInternalError, EHandshakeFailure, EInternalError, 1, "internal error"}, + {EUserCanceled, ECloseNotify, EUserCanceled, 0, "handshake canceled by user"}, + {ENoRenegotiation, EUnexpectedMessage, ENoRenegotiation, 0, "no renegotiation"}, +}; + +enum +{ + /* max. open tls connections */ + MaxTlsDevs = 1024 +}; + +static Lock tdlock; +static int tdhiwat; +static int maxtlsdevs = 128; +static TlsRec **tlsdevs; +static char **trnames; +static char *encalgs; +static char *hashalgs; + +enum{ + Qtopdir = 1, /* top level directory */ + Qprotodir, + Qclonus, + Qencalgs, + Qhashalgs, + Qconvdir, /* directory for a conversation */ + Qdata, + Qctl, + Qhand, + Qstatus, + Qstats, +}; + +#define TYPE(x) ((x).path & 0xf) +#define CONV(x) (((x).path >> 5)&(MaxTlsDevs-1)) +#define QID(c, y) (((c)<<5) | (y)) + +static void checkstate(TlsRec *, int, int); +static void ensure(TlsRec*, Block**, int); +static void consume(Block**, uchar*, int); +static Chan* buftochan(char*); +static void tlshangup(TlsRec*); +static void tlsError(TlsRec*, char *); +static void alertHand(TlsRec*, char *); +static TlsRec *newtls(Chan *c); +static TlsRec *mktlsrec(void); +static DigestState*sslmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s); +static DigestState*sslmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s); +static DigestState*nomac(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s); +static void sslPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac); +static void tlsPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac); +static void put64(uchar *p, vlong x); +static void put32(uchar *p, u32int); +static void put24(uchar *p, int); +static void put16(uchar *p, int); +/* static u32int get32(uchar *p); */ +static int get16(uchar *p); +static void tlsSetState(TlsRec *tr, int new, int old); +static void rcvAlert(TlsRec *tr, int err); +static void sendAlert(TlsRec *tr, int err); +static void rcvError(TlsRec *tr, int err, char *msg, ...); +static int rc4enc(Secret *sec, uchar *buf, int n); +static int des3enc(Secret *sec, uchar *buf, int n); +static int des3dec(Secret *sec, uchar *buf, int n); +static int noenc(Secret *sec, uchar *buf, int n); +static int sslunpad(uchar *buf, int n, int block); +static int tlsunpad(uchar *buf, int n, int block); +static void freeSec(Secret *sec); +static char *tlsstate(int s); +static void pdump(int, void*, char*); + +static char *tlsnames[] = { + /* unused */ 0, + /* topdir */ 0, + /* protodir */ 0, + "clone", + "encalgs", + "hashalgs", + /* convdir */ 0, + "data", + "ctl", + "hand", + "status", + "stats", +}; + +static int convdir[] = { Qctl, Qdata, Qhand, Qstatus, Qstats }; + +static int +tlsgen(Chan *c, char*unused1, Dirtab *unused2, int unused3, int s, Dir *dp) +{ + Qid q; + TlsRec *tr; + char *name, *nm; + int perm, t; + + q.vers = 0; + q.type = QTFILE; + + t = TYPE(c->qid); + switch(t) { + case Qtopdir: + if(s == DEVDOTDOT){ + q.path = QID(0, Qtopdir); + q.type = QTDIR; + devdir(c, q, "#a", 0, eve, 0555, dp); + return 1; + } + if(s > 0) + return -1; + q.path = QID(0, Qprotodir); + q.type = QTDIR; + devdir(c, q, "tls", 0, eve, 0555, dp); + return 1; + case Qprotodir: + if(s == DEVDOTDOT){ + q.path = QID(0, Qtopdir); + q.type = QTDIR; + devdir(c, q, ".", 0, eve, 0555, dp); + return 1; + } + if(s < 3){ + switch(s) { + default: + return -1; + case 0: + q.path = QID(0, Qclonus); + break; + case 1: + q.path = QID(0, Qencalgs); + break; + case 2: + q.path = QID(0, Qhashalgs); + break; + } + perm = 0444; + if(TYPE(q) == Qclonus) + perm = 0555; + devdir(c, q, tlsnames[TYPE(q)], 0, eve, perm, dp); + return 1; + } + s -= 3; + if(s >= tdhiwat) + return -1; + q.path = QID(s, Qconvdir); + q.type = QTDIR; + lock(&tdlock); + tr = tlsdevs[s]; + if(tr != nil) + nm = tr->user; + else + nm = eve; + if((name = trnames[s]) == nil){ + name = trnames[s] = smalloc(16); + sprint(name, "%d", s); + } + devdir(c, q, name, 0, nm, 0555, dp); + unlock(&tdlock); + return 1; + case Qconvdir: + if(s == DEVDOTDOT){ + q.path = QID(0, Qprotodir); + q.type = QTDIR; + devdir(c, q, "tls", 0, eve, 0555, dp); + return 1; + } + if(s < 0 || s >= nelem(convdir)) + return -1; + lock(&tdlock); + tr = tlsdevs[CONV(c->qid)]; + if(tr != nil){ + nm = tr->user; + perm = tr->perm; + }else{ + perm = 0; + nm = eve; + } + t = convdir[s]; + if(t == Qstatus || t == Qstats) + perm &= 0444; + q.path = QID(CONV(c->qid), t); + devdir(c, q, tlsnames[t], 0, nm, perm, dp); + unlock(&tdlock); + return 1; + case Qclonus: + case Qencalgs: + case Qhashalgs: + perm = 0444; + if(t == Qclonus) + perm = 0555; + devdir(c, c->qid, tlsnames[t], 0, eve, perm, dp); + return 1; + default: + lock(&tdlock); + tr = tlsdevs[CONV(c->qid)]; + if(tr != nil){ + nm = tr->user; + perm = tr->perm; + }else{ + perm = 0; + nm = eve; + } + if(t == Qstatus || t == Qstats) + perm &= 0444; + devdir(c, c->qid, tlsnames[t], 0, nm, perm, dp); + unlock(&tdlock); + return 1; + } + return -1; +} + +static Chan* +tlsattach(char *spec) +{ + Chan *c; + + c = devattach('a', spec); + c->qid.path = QID(0, Qtopdir); + c->qid.type = QTDIR; + c->qid.vers = 0; + return c; +} + +static Walkqid* +tlswalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, tlsgen); +} + +static int +tlsstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, nil, 0, tlsgen); +} + +static Chan* +tlsopen(Chan *c, int omode) +{ + TlsRec *tr, **pp; + int t, perm; + + perm = 0; + omode &= 3; + switch(omode) { + case OREAD: + perm = 4; + break; + case OWRITE: + perm = 2; + break; + case ORDWR: + perm = 6; + break; + } + + t = TYPE(c->qid); + switch(t) { + default: + panic("tlsopen"); + case Qtopdir: + case Qprotodir: + case Qconvdir: + if(omode != OREAD) + error(Eperm); + break; + case Qclonus: + tr = newtls(c); + if(tr == nil) + error(Enodev); + break; + case Qctl: + case Qdata: + case Qhand: + case Qstatus: + case Qstats: + if((t == Qstatus || t == Qstats) && omode != OREAD) + error(Eperm); + if(waserror()) { + unlock(&tdlock); + nexterror(); + } + lock(&tdlock); + pp = &tlsdevs[CONV(c->qid)]; + tr = *pp; + if(tr == nil) + error("must open connection using clone"); + if((perm & (tr->perm>>6)) != perm + && (strcmp(up->user, tr->user) != 0 + || (perm & tr->perm) != perm)) + error(Eperm); + if(t == Qhand){ + if(waserror()){ + unlock(&tr->hqlock); + nexterror(); + } + lock(&tr->hqlock); + if(tr->handq != nil) + error(Einuse); + tr->handq = qopen(2 * MaxCipherRecLen, 0, 0, nil); + if(tr->handq == nil) + error("cannot allocate handshake queue"); + tr->hqref = 1; + unlock(&tr->hqlock); + poperror(); + } + tr->ref++; + unlock(&tdlock); + poperror(); + break; + case Qencalgs: + case Qhashalgs: + if(omode != OREAD) + error(Eperm); + break; + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + c->iounit = qiomaxatomic; + return c; +} + +static int +tlswstat(Chan *c, uchar *dp, int n) +{ + Dir *d; + TlsRec *tr; + int rv; + + d = nil; + if(waserror()){ + free(d); + unlock(&tdlock); + nexterror(); + } + + lock(&tdlock); + tr = tlsdevs[CONV(c->qid)]; + if(tr == nil) + error(Ebadusefd); + if(strcmp(tr->user, up->user) != 0) + error(Eperm); + + d = smalloc(n + sizeof *d); + rv = convM2D(dp, n, &d[0], (char*) &d[1]); + if(rv == 0) + error(Eshortstat); + if(!emptystr(d->uid)) + kstrdup(&tr->user, d->uid); + if(d->mode != ~0UL) + tr->perm = d->mode; + + free(d); + poperror(); + unlock(&tdlock); + + return rv; +} + +static void +dechandq(TlsRec *tr) +{ + lock(&tr->hqlock); + if(--tr->hqref == 0){ + if(tr->handq != nil){ + qfree(tr->handq); + tr->handq = nil; + } + if(tr->hprocessed != nil){ + freeb(tr->hprocessed); + tr->hprocessed = nil; + } + } + unlock(&tr->hqlock); +} + +static void +tlsclose(Chan *c) +{ + TlsRec *tr; + int t; + + t = TYPE(c->qid); + switch(t) { + case Qctl: + case Qdata: + case Qhand: + case Qstatus: + case Qstats: + if((c->flag & COPEN) == 0) + break; + + tr = tlsdevs[CONV(c->qid)]; + if(tr == nil) + break; + + if(t == Qhand) + dechandq(tr); + + lock(&tdlock); + if(--tr->ref > 0) { + unlock(&tdlock); + return; + } + tlsdevs[CONV(c->qid)] = nil; + unlock(&tdlock); + + if(tr->c != nil && !waserror()){ + checkstate(tr, 0, SOpen|SHandshake|SRClose); + sendAlert(tr, ECloseNotify); + poperror(); + } + tlshangup(tr); + if(tr->c != nil) + cclose(tr->c); + freeSec(tr->in.sec); + freeSec(tr->in.new); + freeSec(tr->out.sec); + freeSec(tr->out.new); + free(tr->user); + free(tr); + break; + } +} + +/* + * make sure we have at least 'n' bytes in list 'l' + */ +static void +ensure(TlsRec *s, Block **l, int n) +{ + int sofar, i; + Block *b, *bl; + + sofar = 0; + for(b = *l; b; b = b->next){ + sofar += BLEN(b); + if(sofar >= n) + return; + l = &b->next; + } + + while(sofar < n){ + bl = devtab[s->c->type]->bread(s->c, MaxCipherRecLen + RecHdrLen, 0); + if(bl == 0) + error(Ehungup); + *l = bl; + i = 0; + for(b = bl; b; b = b->next){ + i += BLEN(b); + l = &b->next; + } + if(i == 0) + error(Ehungup); + sofar += i; + } +if(s->debug) pprint("ensure read %d\n", sofar); +} + +/* + * copy 'n' bytes from 'l' into 'p' and free + * the bytes in 'l' + */ +static void +consume(Block **l, uchar *p, int n) +{ + Block *b; + int i; + + for(; *l && n > 0; n -= i){ + b = *l; + i = BLEN(b); + if(i > n) + i = n; + memmove(p, b->rp, i); + b->rp += i; + p += i; + if(BLEN(b) < 0) + panic("consume"); + if(BLEN(b)) + break; + *l = b->next; + freeb(b); + } +} + +/* + * give back n bytes + */ +static void +regurgitate(TlsRec *s, uchar *p, int n) +{ + Block *b; + + if(n <= 0) + return; + b = s->unprocessed; + if(s->unprocessed == nil || b->rp - b->base < n) { + b = allocb(n); + memmove(b->wp, p, n); + b->wp += n; + b->next = s->unprocessed; + s->unprocessed = b; + } else { + b->rp -= n; + memmove(b->rp, p, n); + } +} + +/* + * remove at most n bytes from the queue + */ +static Block* +qgrab(Block **l, int n) +{ + Block *bb, *b; + int i; + + b = *l; + if(BLEN(b) == n){ + *l = b->next; + b->next = nil; + return b; + } + + i = 0; + for(bb = b; bb != nil && i < n; bb = bb->next) + i += BLEN(bb); + if(i > n) + i = n; + + bb = allocb(i); + consume(l, bb->wp, i); + bb->wp += i; + return bb; +} + +static void +tlsclosed(TlsRec *tr, int new) +{ + lock(&tr->statelk); + if(tr->state == SOpen || tr->state == SHandshake) + tr->state = new; + else if((new | tr->state) == (SRClose|SLClose)) + tr->state = SClosed; + unlock(&tr->statelk); + alertHand(tr, "close notify"); +} + +/* + * read and process one tls record layer message + * must be called with tr->in.io held + * We can't let Eintrs lose data, since doing so will get + * us out of sync with the sender and break the reliablity + * of the channel. Eintr only happens during the reads in + * consume. Therefore we put back any bytes consumed before + * the last call to ensure. + */ +static void +tlsrecread(TlsRec *tr) +{ + OneWay *volatile in; + Block *volatile b; + uchar *p, seq[8], header[RecHdrLen], hmac[MD5dlen]; + int volatile nconsumed; + int len, type, ver, unpad_len; + + nconsumed = 0; + if(waserror()){ + if(strcmp(up->errstr, Eintr) == 0 && !waserror()){ + regurgitate(tr, header, nconsumed); + poperror(); + }else + tlsError(tr, "channel error"); + nexterror(); + } + ensure(tr, &tr->unprocessed, RecHdrLen); + consume(&tr->unprocessed, header, RecHdrLen); +if(tr->debug)pprint("consumed %d header\n", RecHdrLen); + nconsumed = RecHdrLen; + + if((tr->handin == 0) && (header[0] & 0x80)){ + /* Cope with an SSL3 ClientHello expressed in SSL2 record format. + This is sent by some clients that we must interoperate + with, such as Java's JSSE and Microsoft's Internet Explorer. */ + len = (get16(header) & ~0x8000) - 3; + type = header[2]; + ver = get16(header + 3); + if(type != SSL2ClientHello || len < 22) + rcvError(tr, EProtocolVersion, "invalid initial SSL2-like message"); + }else{ /* normal SSL3 record format */ + type = header[0]; + ver = get16(header+1); + len = get16(header+3); + } + if(ver != tr->version && (tr->verset || ver < MinProtoVersion || ver > MaxProtoVersion)) + rcvError(tr, EProtocolVersion, "devtls expected ver=%x%s, saw (len=%d) type=%x ver=%x '%.12s'", + tr->version, tr->verset?"/set":"", len, type, ver, (char*)header); + if(len > MaxCipherRecLen || len < 0) + rcvError(tr, ERecordOverflow, "record message too long %d", len); + ensure(tr, &tr->unprocessed, len); + nconsumed = 0; + poperror(); + + /* + * If an Eintr happens after this, we'll get out of sync. + * Make sure nothing we call can sleep. + * Errors are ok, as they kill the connection. + * Luckily, allocb won't sleep, it'll just error out. + */ + b = nil; + if(waserror()){ + if(b != nil) + freeb(b); + tlsError(tr, "channel error"); + nexterror(); + } + b = qgrab(&tr->unprocessed, len); +if(tr->debug) pprint("consumed unprocessed %d\n", len); + + in = &tr->in; + if(waserror()){ + qunlock(&in->seclock); + nexterror(); + } + qlock(&in->seclock); + p = b->rp; + if(in->sec != nil) { + /* to avoid Canvel-Hiltgen-Vaudenay-Vuagnoux attack, all errors here + should look alike, including timing of the response. */ + unpad_len = (*in->sec->dec)(in->sec, p, len); + if(unpad_len >= in->sec->maclen) + len = unpad_len - in->sec->maclen; +if(tr->debug) pprint("decrypted %d\n", unpad_len); +if(tr->debug) pdump(unpad_len, p, "decrypted:"); + + /* update length */ + put16(header+3, len); + put64(seq, in->seq); + in->seq++; + (*tr->packMac)(in->sec, in->sec->mackey, seq, header, p, len, hmac); + if(unpad_len < in->sec->maclen) + rcvError(tr, EBadRecordMac, "short record mac"); + if(memcmp(hmac, p+len, in->sec->maclen) != 0) + rcvError(tr, EBadRecordMac, "record mac mismatch"); + b->wp = b->rp + len; + } + qunlock(&in->seclock); + poperror(); + if(len < 0) + rcvError(tr, EDecodeError, "runt record message"); + + switch(type) { + default: + rcvError(tr, EIllegalParameter, "invalid record message 0x%x", type); + break; + case RChangeCipherSpec: + if(len != 1 || p[0] != 1) + rcvError(tr, EDecodeError, "invalid change cipher spec"); + qlock(&in->seclock); + if(in->new == nil){ + qunlock(&in->seclock); + rcvError(tr, EUnexpectedMessage, "unexpected change cipher spec"); + } + freeSec(in->sec); + in->sec = in->new; + in->new = nil; + in->seq = 0; + qunlock(&in->seclock); + break; + case RAlert: + if(len != 2) + rcvError(tr, EDecodeError, "invalid alert"); + if(p[0] == 2) + rcvAlert(tr, p[1]); + if(p[0] != 1) + rcvError(tr, EIllegalParameter, "invalid alert fatal code"); + + /* + * propate non-fatal alerts to handshaker + */ + if(p[1] == ECloseNotify) { + tlsclosed(tr, SRClose); + if(tr->opened) + error("tls hungup"); + error("close notify"); + } + if(p[1] == ENoRenegotiation) + alertHand(tr, "no renegotiation"); + else if(p[1] == EUserCanceled) + alertHand(tr, "handshake canceled by user"); + else + rcvError(tr, EIllegalParameter, "invalid alert code"); + break; + case RHandshake: + /* + * don't worry about dropping the block + * qbwrite always queues even if flow controlled and interrupted. + * + * if there isn't any handshaker, ignore the request, + * but notify the other side we are doing so. + */ + lock(&tr->hqlock); + if(tr->handq != nil){ + tr->hqref++; + unlock(&tr->hqlock); + if(waserror()){ + dechandq(tr); + nexterror(); + } + b = padblock(b, 1); + *b->rp = RHandshake; + qbwrite(tr->handq, b); + b = nil; + poperror(); + dechandq(tr); + }else{ + unlock(&tr->hqlock); + if(tr->verset && tr->version != SSL3Version && !waserror()){ + sendAlert(tr, ENoRenegotiation); + poperror(); + } + } + break; + case SSL2ClientHello: + lock(&tr->hqlock); + if(tr->handq != nil){ + tr->hqref++; + unlock(&tr->hqlock); + if(waserror()){ + dechandq(tr); + nexterror(); + } + /* Pass the SSL2 format data, so that the handshake code can compute + the correct checksums. HSSL2ClientHello = HandshakeType 9 is + unused in RFC2246. */ + b = padblock(b, 8); + b->rp[0] = RHandshake; + b->rp[1] = HSSL2ClientHello; + put24(&b->rp[2], len+3); + b->rp[5] = SSL2ClientHello; + put16(&b->rp[6], ver); + qbwrite(tr->handq, b); + b = nil; + poperror(); + dechandq(tr); + }else{ + unlock(&tr->hqlock); + if(tr->verset && tr->version != SSL3Version && !waserror()){ + sendAlert(tr, ENoRenegotiation); + poperror(); + } + } + break; + case RApplication: + if(!tr->opened) + rcvError(tr, EUnexpectedMessage, "application message received before handshake completed"); + if(BLEN(b) > 0){ + tr->processed = b; + b = nil; + } + break; + } + if(b != nil) + freeb(b); + poperror(); +} + +/* + * got a fatal alert message + */ +static void +rcvAlert(TlsRec *tr, int err) +{ + char *s; + int i; + + s = "unknown error"; + for(i=0; i < nelem(tlserrs); i++){ + if(tlserrs[i].err == err){ + s = tlserrs[i].msg; + break; + } + } +if(tr->debug) pprint("rcvAlert: %s\n", s); + + tlsError(tr, s); + if(!tr->opened) + error(s); + error("tls error"); +} + +/* + * found an error while decoding the input stream + */ +static void +rcvError(TlsRec *tr, int err, char *fmt, ...) +{ + char msg[ERRMAX]; + va_list arg; + + va_start(arg, fmt); + vseprint(msg, msg+sizeof(msg), fmt, arg); + va_end(arg); +if(tr->debug) pprint("rcvError: %s\n", msg); + + sendAlert(tr, err); + + if(!tr->opened) + error(msg); + error("tls error"); +} + +/* + * make sure the next hand operation returns with a 'msg' error + */ +static void +alertHand(TlsRec *tr, char *msg) +{ + Block *b; + int n; + + lock(&tr->hqlock); + if(tr->handq == nil){ + unlock(&tr->hqlock); + return; + } + tr->hqref++; + unlock(&tr->hqlock); + + n = strlen(msg); + if(waserror()){ + dechandq(tr); + nexterror(); + } + b = allocb(n + 2); + *b->wp++ = RAlert; + memmove(b->wp, msg, n + 1); + b->wp += n + 1; + + qbwrite(tr->handq, b); + + poperror(); + dechandq(tr); +} + +static void +checkstate(TlsRec *tr, int ishand, int ok) +{ + int state; + + lock(&tr->statelk); + state = tr->state; + unlock(&tr->statelk); + if(state & ok) + return; + switch(state){ + case SHandshake: + case SOpen: + break; + case SError: + case SAlert: + if(ishand) + error(tr->err); + error("tls error"); + case SRClose: + case SLClose: + case SClosed: + error("tls hungup"); + } + error("tls improperly configured"); +} + +static Block* +tlsbread(Chan *c, long n, ulong offset) +{ + int ty; + Block *b; + TlsRec *volatile tr; + + ty = TYPE(c->qid); + switch(ty) { + default: + return devbread(c, n, offset); + case Qhand: + case Qdata: + break; + } + + tr = tlsdevs[CONV(c->qid)]; + if(tr == nil) + panic("tlsbread"); + + if(waserror()){ + qunlock(&tr->in.io); + nexterror(); + } + qlock(&tr->in.io); + if(ty == Qdata){ + checkstate(tr, 0, SOpen); + while(tr->processed == nil) + tlsrecread(tr); + + /* return at most what was asked for */ + b = qgrab(&tr->processed, n); +if(tr->debug) pprint("consumed processed %d\n", BLEN(b)); +if(tr->debug) pdump(BLEN(b), b->rp, "consumed:"); + qunlock(&tr->in.io); + poperror(); + tr->datain += BLEN(b); + }else{ + checkstate(tr, 1, SOpen|SHandshake|SLClose); + + /* + * it's ok to look at state without the lock + * since it only protects reading records, + * and we have that tr->in.io held. + */ + while(!tr->opened && tr->hprocessed == nil && !qcanread(tr->handq)) + tlsrecread(tr); + + qunlock(&tr->in.io); + poperror(); + + if(waserror()){ + qunlock(&tr->hqread); + nexterror(); + } + qlock(&tr->hqread); + if(tr->hprocessed == nil){ + b = qbread(tr->handq, MaxRecLen + 1); + if(*b->rp++ == RAlert){ + kstrcpy(up->errstr, (char*)b->rp, ERRMAX); + freeb(b); + nexterror(); + } + tr->hprocessed = b; + } + b = qgrab(&tr->hprocessed, n); + poperror(); + qunlock(&tr->hqread); + tr->handin += BLEN(b); + } + + return b; +} + +static long +tlsread(Chan *c, void *a, long n, vlong off) +{ + Block *volatile b; + Block *nb; + uchar *va; + int i, ty; + char *buf, *s, *e; + ulong offset = off; + TlsRec * tr; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, tlsgen); + + tr = tlsdevs[CONV(c->qid)]; + ty = TYPE(c->qid); + switch(ty) { + default: + error(Ebadusefd); + case Qstatus: + buf = smalloc(Statlen); + qlock(&tr->in.seclock); + qlock(&tr->out.seclock); + s = buf; + e = buf + Statlen; + s = seprint(s, e, "State: %s\n", tlsstate(tr->state)); + s = seprint(s, e, "Version: 0x%x\n", tr->version); + if(tr->in.sec != nil) + s = seprint(s, e, "EncIn: %s\nHashIn: %s\n", tr->in.sec->encalg, tr->in.sec->hashalg); + if(tr->in.new != nil) + s = seprint(s, e, "NewEncIn: %s\nNewHashIn: %s\n", tr->in.new->encalg, tr->in.new->hashalg); + if(tr->out.sec != nil) + s = seprint(s, e, "EncOut: %s\nHashOut: %s\n", tr->out.sec->encalg, tr->out.sec->hashalg); + if(tr->out.new != nil) + seprint(s, e, "NewEncOut: %s\nNewHashOut: %s\n", tr->out.new->encalg, tr->out.new->hashalg); + qunlock(&tr->in.seclock); + qunlock(&tr->out.seclock); + n = readstr(offset, a, n, buf); + free(buf); + return n; + case Qstats: + buf = smalloc(Statlen); + s = buf; + e = buf + Statlen; + s = seprint(s, e, "DataIn: %lld\n", tr->datain); + s = seprint(s, e, "DataOut: %lld\n", tr->dataout); + s = seprint(s, e, "HandIn: %lld\n", tr->handin); + seprint(s, e, "HandOut: %lld\n", tr->handout); + n = readstr(offset, a, n, buf); + free(buf); + return n; + case Qctl: + buf = smalloc(Statlen); + snprint(buf, Statlen, "%llud", CONV(c->qid)); + n = readstr(offset, a, n, buf); + free(buf); + return n; + case Qdata: + case Qhand: + b = tlsbread(c, n, offset); + break; + case Qencalgs: + return readstr(offset, a, n, encalgs); + case Qhashalgs: + return readstr(offset, a, n, hashalgs); + } + + if(waserror()){ + freeblist(b); + nexterror(); + } + + n = 0; + va = a; + for(nb = b; nb; nb = nb->next){ + i = BLEN(nb); + memmove(va+n, nb->rp, i); + n += i; + } + + freeblist(b); + poperror(); + + return n; +} + +/* + * write a block in tls records + */ +static void +tlsrecwrite(TlsRec *tr, int type, Block *b) +{ + Block *volatile bb; + Block *nb; + uchar *p, seq[8]; + OneWay *volatile out; + int n, maclen, pad, ok; + + out = &tr->out; + bb = b; + if(waserror()){ + qunlock(&out->io); + if(bb != nil) + freeb(bb); + nexterror(); + } + qlock(&out->io); +if(tr->debug)pprint("send %d\n", BLEN(b)); +if(tr->debug)pdump(BLEN(b), b->rp, "sent:"); + + + ok = SHandshake|SOpen|SRClose; + if(type == RAlert) + ok |= SAlert; + while(bb != nil){ + checkstate(tr, type != RApplication, ok); + + /* + * get at most one maximal record's input, + * with padding on the front for header and + * back for mac and maximal block padding. + */ + if(waserror()){ + qunlock(&out->seclock); + nexterror(); + } + qlock(&out->seclock); + maclen = 0; + pad = 0; + if(out->sec != nil){ + maclen = out->sec->maclen; + pad = maclen + out->sec->block; + } + n = BLEN(bb); + if(n > MaxRecLen){ + n = MaxRecLen; + nb = allocb(n + pad + RecHdrLen); + memmove(nb->wp + RecHdrLen, bb->rp, n); + bb->rp += n; + }else{ + /* + * carefully reuse bb so it will get freed if we're out of memory + */ + bb = padblock(bb, RecHdrLen); + if(pad) + nb = padblock(bb, -pad); + else + nb = bb; + bb = nil; + } + + p = nb->rp; + p[0] = type; + put16(p+1, tr->version); + put16(p+3, n); + + if(out->sec != nil){ + put64(seq, out->seq); + out->seq++; + (*tr->packMac)(out->sec, out->sec->mackey, seq, p, p + RecHdrLen, n, p + RecHdrLen + n); + n += maclen; + + /* encrypt */ + n = (*out->sec->enc)(out->sec, p + RecHdrLen, n); + nb->wp = p + RecHdrLen + n; + + /* update length */ + put16(p+3, n); + } + if(type == RChangeCipherSpec){ + if(out->new == nil) + error("change cipher without a new cipher"); + freeSec(out->sec); + out->sec = out->new; + out->new = nil; + out->seq = 0; + } + qunlock(&out->seclock); + poperror(); + + /* + * if bwrite error's, we assume the block is queued. + * if not, we're out of sync with the receiver and will not recover. + */ + if(waserror()){ + if(strcmp(up->errstr, "interrupted") != 0) + tlsError(tr, "channel error"); + nexterror(); + } + devtab[tr->c->type]->bwrite(tr->c, nb, 0); + poperror(); + } + qunlock(&out->io); + poperror(); +} + +static long +tlsbwrite(Chan *c, Block *b, ulong offset) +{ + int ty; + ulong n; + TlsRec *tr; + + n = BLEN(b); + + tr = tlsdevs[CONV(c->qid)]; + if(tr == nil) + panic("tlsbread"); + + ty = TYPE(c->qid); + switch(ty) { + default: + return devbwrite(c, b, offset); + case Qhand: + tlsrecwrite(tr, RHandshake, b); + tr->handout += n; + break; + case Qdata: + checkstate(tr, 0, SOpen); + tlsrecwrite(tr, RApplication, b); + tr->dataout += n; + break; + } + + return n; +} + +typedef struct Hashalg Hashalg; +struct Hashalg +{ + char *name; + int maclen; + void (*initkey)(Hashalg *, int, Secret *, uchar*); +}; + +static void +initmd5key(Hashalg *ha, int version, Secret *s, uchar *p) +{ + s->maclen = ha->maclen; + if(version == SSL3Version) + s->mac = sslmac_md5; + else + s->mac = hmac_md5; + memmove(s->mackey, p, ha->maclen); +} + +static void +initclearmac(Hashalg *unused1, int unused2, Secret *s, uchar *unused3) +{ + s->maclen = 0; + s->mac = nomac; +} + +static void +initsha1key(Hashalg *ha, int version, Secret *s, uchar *p) +{ + s->maclen = ha->maclen; + if(version == SSL3Version) + s->mac = sslmac_sha1; + else + s->mac = hmac_sha1; + memmove(s->mackey, p, ha->maclen); +} + +static Hashalg hashtab[] = +{ + { "clear", 0, initclearmac, }, + { "md5", MD5dlen, initmd5key, }, + { "sha1", SHA1dlen, initsha1key, }, + { 0 } +}; + +static Hashalg* +parsehashalg(char *p) +{ + Hashalg *ha; + + for(ha = hashtab; ha->name; ha++) + if(strcmp(p, ha->name) == 0) + return ha; + error("unsupported hash algorithm"); + return nil; +} + +typedef struct Encalg Encalg; +struct Encalg +{ + char *name; + int keylen; + int ivlen; + void (*initkey)(Encalg *ea, Secret *, uchar*, uchar*); +}; + +static void +initRC4key(Encalg *ea, Secret *s, uchar *p, uchar *unused1) +{ + s->enckey = smalloc(sizeof(RC4state)); + s->enc = rc4enc; + s->dec = rc4enc; + s->block = 0; + setupRC4state(s->enckey, p, ea->keylen); +} + +static void +initDES3key(Encalg *unused1, Secret *s, uchar *p, uchar *iv) +{ + s->enckey = smalloc(sizeof(DES3state)); + s->enc = des3enc; + s->dec = des3dec; + s->block = 8; + setupDES3state(s->enckey, (uchar(*)[8])p, iv); +} + +static void +initclearenc(Encalg *unused1, Secret *s, uchar *unused2, uchar *unused3) +{ + s->enc = noenc; + s->dec = noenc; + s->block = 0; +} + +static Encalg encrypttab[] = +{ + { "clear", 0, 0, initclearenc }, + { "rc4_128", 128/8, 0, initRC4key }, + { "3des_ede_cbc", 3 * 8, 8, initDES3key }, + { 0 } +}; + +static Encalg* +parseencalg(char *p) +{ + Encalg *ea; + + for(ea = encrypttab; ea->name; ea++) + if(strcmp(p, ea->name) == 0) + return ea; + error("unsupported encryption algorithm"); + return nil; +} + +static long +tlswrite(Chan *c, void *a, long n, vlong off) +{ + Encalg *ea; + Hashalg *ha; + TlsRec *volatile tr; + Secret *volatile tos, *volatile toc; + Block *volatile b; + Cmdbuf *volatile cb; + int m, ty; + char *p, *e; + uchar *volatile x; + ulong offset = off; + + tr = tlsdevs[CONV(c->qid)]; + if(tr == nil) + panic("tlswrite"); + + ty = TYPE(c->qid); + switch(ty){ + case Qdata: + case Qhand: + p = a; + e = p + n; + do{ + m = e - p; + if(m > MaxRecLen) + m = MaxRecLen; + + b = allocb(m); + if(waserror()){ + freeb(b); + nexterror(); + } + memmove(b->wp, p, m); + poperror(); + b->wp += m; + + tlsbwrite(c, b, offset); + + p += m; + }while(p < e); + return n; + case Qctl: + break; + default: + error(Ebadusefd); + return -1; + } + + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + if(cb->nf < 1) + error("short control request"); + + /* mutex with operations using what we're about to change */ + if(waserror()){ + qunlock(&tr->in.seclock); + qunlock(&tr->out.seclock); + nexterror(); + } + qlock(&tr->in.seclock); + qlock(&tr->out.seclock); + + if(strcmp(cb->f[0], "fd") == 0){ + if(cb->nf != 3) + error("usage: fd open-fd version"); + if(tr->c != nil) + error(Einuse); + m = strtol(cb->f[2], nil, 0); + if(m < MinProtoVersion || m > MaxProtoVersion) + error("unsupported version"); + tr->c = buftochan(cb->f[1]); + tr->version = m; + tlsSetState(tr, SHandshake, SClosed); + }else if(strcmp(cb->f[0], "version") == 0){ + if(cb->nf != 2) + error("usage: version vers"); + if(tr->c == nil) + error("must set fd before version"); + if(tr->verset) + error("version already set"); + m = strtol(cb->f[1], nil, 0); + if(m == SSL3Version) + tr->packMac = sslPackMac; + else if(m == TLSVersion) + tr->packMac = tlsPackMac; + else + error("unsupported version"); + tr->verset = 1; + tr->version = m; + }else if(strcmp(cb->f[0], "secret") == 0){ + if(cb->nf != 5) + error("usage: secret hashalg encalg isclient secretdata"); + if(tr->c == nil || !tr->verset) + error("must set fd and version before secrets"); + + if(tr->in.new != nil){ + freeSec(tr->in.new); + tr->in.new = nil; + } + if(tr->out.new != nil){ + freeSec(tr->out.new); + tr->out.new = nil; + } + + ha = parsehashalg(cb->f[1]); + ea = parseencalg(cb->f[2]); + + p = cb->f[4]; + m = (strlen(p)*3)/2; + x = smalloc(m); + tos = nil; + toc = nil; + if(waserror()){ + freeSec(tos); + freeSec(toc); + free(x); + nexterror(); + } + m = dec64(x, m, p, strlen(p)); + if(m < 2 * ha->maclen + 2 * ea->keylen + 2 * ea->ivlen) + error("not enough secret data provided"); + + tos = smalloc(sizeof(Secret)); + toc = smalloc(sizeof(Secret)); + if(!ha->initkey || !ea->initkey) + error("misimplemented secret algorithm"); + (*ha->initkey)(ha, tr->version, tos, &x[0]); + (*ha->initkey)(ha, tr->version, toc, &x[ha->maclen]); + (*ea->initkey)(ea, tos, &x[2 * ha->maclen], &x[2 * ha->maclen + 2 * ea->keylen]); + (*ea->initkey)(ea, toc, &x[2 * ha->maclen + ea->keylen], &x[2 * ha->maclen + 2 * ea->keylen + ea->ivlen]); + + if(!tos->mac || !tos->enc || !tos->dec + || !toc->mac || !toc->enc || !toc->dec) + error("missing algorithm implementations"); + if(strtol(cb->f[3], nil, 0) == 0){ + tr->in.new = tos; + tr->out.new = toc; + }else{ + tr->in.new = toc; + tr->out.new = tos; + } + if(tr->version == SSL3Version){ + toc->unpad = sslunpad; + tos->unpad = sslunpad; + }else{ + toc->unpad = tlsunpad; + tos->unpad = tlsunpad; + } + toc->encalg = ea->name; + toc->hashalg = ha->name; + tos->encalg = ea->name; + tos->hashalg = ha->name; + + free(x); + poperror(); + }else if(strcmp(cb->f[0], "changecipher") == 0){ + if(cb->nf != 1) + error("usage: changecipher"); + if(tr->out.new == nil) + error("cannot change cipher spec without setting secret"); + + qunlock(&tr->in.seclock); + qunlock(&tr->out.seclock); + poperror(); + free(cb); + poperror(); + + /* + * the real work is done as the message is written + * so the stream is encrypted in sync. + */ + b = allocb(1); + *b->wp++ = 1; + tlsrecwrite(tr, RChangeCipherSpec, b); + return n; + }else if(strcmp(cb->f[0], "opened") == 0){ + if(cb->nf != 1) + error("usage: opened"); + if(tr->in.sec == nil || tr->out.sec == nil) + error("cipher must be configured before enabling data messages"); + lock(&tr->statelk); + if(tr->state != SHandshake && tr->state != SOpen){ + unlock(&tr->statelk); + error("cannot enable data messages"); + } + tr->state = SOpen; + unlock(&tr->statelk); + tr->opened = 1; + }else if(strcmp(cb->f[0], "alert") == 0){ + if(cb->nf != 2) + error("usage: alert n"); + if(tr->c == nil) + error("must set fd before sending alerts"); + m = strtol(cb->f[1], nil, 0); + + qunlock(&tr->in.seclock); + qunlock(&tr->out.seclock); + poperror(); + free(cb); + poperror(); + + sendAlert(tr, m); + + if(m == ECloseNotify) + tlsclosed(tr, SLClose); + + return n; + } else if(strcmp(cb->f[0], "debug") == 0){ + if(cb->nf == 2){ + if(strcmp(cb->f[1], "on") == 0) + tr->debug = 1; + else + tr->debug = 0; + } else + tr->debug = 1; + } else + error(Ebadarg); + + qunlock(&tr->in.seclock); + qunlock(&tr->out.seclock); + poperror(); + free(cb); + poperror(); + + return n; +} + +static void +tlsinit(void) +{ + struct Encalg *e; + struct Hashalg *h; + int n; + char *cp; + static int already; + + if(!already){ + fmtinstall('H', encodefmt); + already = 1; + } + + tlsdevs = smalloc(sizeof(TlsRec*) * maxtlsdevs); + trnames = smalloc((sizeof *trnames) * maxtlsdevs); + + n = 1; + for(e = encrypttab; e->name != nil; e++) + n += strlen(e->name) + 1; + cp = encalgs = smalloc(n); + for(e = encrypttab;;){ + strcpy(cp, e->name); + cp += strlen(e->name); + e++; + if(e->name == nil) + break; + *cp++ = ' '; + } + *cp = 0; + + n = 1; + for(h = hashtab; h->name != nil; h++) + n += strlen(h->name) + 1; + cp = hashalgs = smalloc(n); + for(h = hashtab;;){ + strcpy(cp, h->name); + cp += strlen(h->name); + h++; + if(h->name == nil) + break; + *cp++ = ' '; + } + *cp = 0; +} + +Dev tlsdevtab = { + 'a', + "tls", + + devreset, + tlsinit, + devshutdown, + tlsattach, + tlswalk, + tlsstat, + tlsopen, + devcreate, + tlsclose, + tlsread, + tlsbread, + tlswrite, + tlsbwrite, + devremove, + tlswstat, +}; + +/* get channel associated with an fd */ +static Chan* +buftochan(char *p) +{ + Chan *c; + int fd; + + if(p == 0) + error(Ebadarg); + fd = strtoul(p, 0, 0); + if(fd < 0) + error(Ebadarg); + c = fdtochan(fd, -1, 0, 1); /* error check and inc ref */ + return c; +} + +static void +sendAlert(TlsRec *tr, int err) +{ + Block *b; + int i, fatal; + char *msg; + +if(tr->debug)pprint("sendAlert %d\n", err); + fatal = 1; + msg = "tls unknown alert"; + for(i=0; i < nelem(tlserrs); i++) { + if(tlserrs[i].err == err) { + msg = tlserrs[i].msg; + if(tr->version == SSL3Version) + err = tlserrs[i].sslerr; + else + err = tlserrs[i].tlserr; + fatal = tlserrs[i].fatal; + break; + } + } + + if(!waserror()){ + b = allocb(2); + *b->wp++ = fatal + 1; + *b->wp++ = err; + if(fatal) + tlsSetState(tr, SAlert, SOpen|SHandshake|SRClose); + tlsrecwrite(tr, RAlert, b); + poperror(); + } + if(fatal) + tlsError(tr, msg); +} + +static void +tlsError(TlsRec *tr, char *msg) +{ + int s; + +if(tr->debug)pprint("tleError %s\n", msg); + lock(&tr->statelk); + s = tr->state; + tr->state = SError; + if(s != SError){ + strncpy(tr->err, msg, ERRMAX - 1); + tr->err[ERRMAX - 1] = '\0'; + } + unlock(&tr->statelk); + if(s != SError) + alertHand(tr, msg); +} + +static void +tlsSetState(TlsRec *tr, int new, int old) +{ + lock(&tr->statelk); + if(tr->state & old) + tr->state = new; + unlock(&tr->statelk); +} + +/* hand up a digest connection */ +static void +tlshangup(TlsRec *tr) +{ + Block *b; + + qlock(&tr->in.io); + for(b = tr->processed; b; b = tr->processed){ + tr->processed = b->next; + freeb(b); + } + if(tr->unprocessed != nil){ + freeb(tr->unprocessed); + tr->unprocessed = nil; + } + qunlock(&tr->in.io); + + tlsSetState(tr, SClosed, ~0); +} + +static TlsRec* +newtls(Chan *ch) +{ + TlsRec **pp, **ep, **np; + char **nmp; + int t, newmax; + + if(waserror()) { + unlock(&tdlock); + nexterror(); + } + lock(&tdlock); + ep = &tlsdevs[maxtlsdevs]; + for(pp = tlsdevs; pp < ep; pp++) + if(*pp == nil) + break; + if(pp >= ep) { + if(maxtlsdevs >= MaxTlsDevs) { + unlock(&tdlock); + poperror(); + return nil; + } + newmax = 2 * maxtlsdevs; + if(newmax > MaxTlsDevs) + newmax = MaxTlsDevs; + np = smalloc(sizeof(TlsRec*) * newmax); + memmove(np, tlsdevs, sizeof(TlsRec*) * maxtlsdevs); + tlsdevs = np; + pp = &tlsdevs[maxtlsdevs]; + memset(pp, 0, sizeof(TlsRec*)*(newmax - maxtlsdevs)); + + nmp = smalloc(sizeof *nmp * newmax); + memmove(nmp, trnames, sizeof *nmp * maxtlsdevs); + trnames = nmp; + + maxtlsdevs = newmax; + } + *pp = mktlsrec(); + if(pp - tlsdevs >= tdhiwat) + tdhiwat++; + t = TYPE(ch->qid); + if(t == Qclonus) + t = Qctl; + ch->qid.path = QID(pp - tlsdevs, t); + ch->qid.vers = 0; + unlock(&tdlock); + poperror(); + return *pp; +} + +static TlsRec * +mktlsrec(void) +{ + TlsRec *tr; + + tr = mallocz(sizeof(*tr), 1); + if(tr == nil) + error(Enomem); + tr->state = SClosed; + tr->ref = 1; + kstrdup(&tr->user, up->user); + tr->perm = 0660; + return tr; +} + +static char* +tlsstate(int s) +{ + switch(s){ + case SHandshake: + return "Handshaking"; + case SOpen: + return "Established"; + case SRClose: + return "RemoteClosed"; + case SLClose: + return "LocalClosed"; + case SAlert: + return "Alerting"; + case SError: + return "Errored"; + case SClosed: + return "Closed"; + } + return "Unknown"; +} + +static void +freeSec(Secret *s) +{ + if(s != nil){ + free(s->enckey); + free(s); + } +} + +static int +noenc(Secret *unused1, uchar *unused2, int n) +{ + return n; +} + +static int +rc4enc(Secret *sec, uchar *buf, int n) +{ + rc4(sec->enckey, buf, n); + return n; +} + +static int +tlsunpad(uchar *buf, int n, int block) +{ + int pad, nn; + + pad = buf[n - 1]; + nn = n - 1 - pad; + if(nn <= 0 || n % block) + return -1; + while(--n > nn) + if(pad != buf[n - 1]) + return -1; + return nn; +} + +static int +sslunpad(uchar *buf, int n, int block) +{ + int pad, nn; + + pad = buf[n - 1]; + nn = n - 1 - pad; + if(nn <= 0 || n % block) + return -1; + return nn; +} + +static int +blockpad(uchar *buf, int n, int block) +{ + int pad, nn; + + nn = n + block; + nn -= nn % block; + pad = nn - (n + 1); + while(n < nn) + buf[n++] = pad; + return nn; +} + +static int +des3enc(Secret *sec, uchar *buf, int n) +{ + n = blockpad(buf, n, 8); + des3CBCencrypt(buf, n, sec->enckey); + return n; +} + +static int +des3dec(Secret *sec, uchar *buf, int n) +{ + des3CBCdecrypt(buf, n, sec->enckey); + return (*sec->unpad)(buf, n, 8); +} +static DigestState* +nomac(uchar *unused1, ulong unused2, uchar *unused3, ulong unused4, + uchar *unused5, DigestState *unused6) +{ + return nil; +} + +/* + * sslmac: mac calculations for ssl 3.0 only; tls 1.0 uses the standard hmac. + */ +static DigestState* +sslmac_x(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s, + DigestState*(*x)(uchar*, ulong, uchar*, DigestState*), int xlen, int padlen) +{ + int i; + uchar pad[48], innerdigest[20]; + + if(xlen > sizeof(innerdigest) + || padlen > sizeof(pad)) + return nil; + + if(klen>64) + return nil; + + /* first time through */ + if(s == nil){ + for(i=0; i<padlen; i++) + pad[i] = 0x36; + s = (*x)(key, klen, nil, nil); + s = (*x)(pad, padlen, nil, s); + if(s == nil) + return nil; + } + + s = (*x)(p, len, nil, s); + if(digest == nil) + return s; + + /* last time through */ + for(i=0; i<padlen; i++) + pad[i] = 0x5c; + (*x)(nil, 0, innerdigest, s); + s = (*x)(key, klen, nil, nil); + s = (*x)(pad, padlen, nil, s); + (*x)(innerdigest, xlen, digest, s); + return nil; +} + +static DigestState* +sslmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s) +{ + return sslmac_x(p, len, key, klen, digest, s, sha1, SHA1dlen, 40); +} + +static DigestState* +sslmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s) +{ + return sslmac_x(p, len, key, klen, digest, s, md5, MD5dlen, 48); +} + +static void +sslPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac) +{ + DigestState *s; + uchar buf[11]; + + memmove(buf, seq, 8); + buf[8] = header[0]; + buf[9] = header[3]; + buf[10] = header[4]; + + s = (*sec->mac)(buf, 11, mackey, sec->maclen, 0, 0); + (*sec->mac)(body, len, mackey, sec->maclen, mac, s); +} + +static void +tlsPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac) +{ + DigestState *s; + uchar buf[13]; + + memmove(buf, seq, 8); + memmove(&buf[8], header, 5); + + s = (*sec->mac)(buf, 13, mackey, sec->maclen, 0, 0); + (*sec->mac)(body, len, mackey, sec->maclen, mac, s); +} + +static void +put32(uchar *p, u32int x) +{ + p[0] = x>>24; + p[1] = x>>16; + p[2] = x>>8; + p[3] = x; +} + +static void +put64(uchar *p, vlong x) +{ + put32(p, (u32int)(x >> 32)); + put32(p+4, (u32int)x); +} + +static void +put24(uchar *p, int x) +{ + p[0] = x>>16; + p[1] = x>>8; + p[2] = x; +} + +static void +put16(uchar *p, int x) +{ + p[0] = x>>8; + p[1] = x; +} + +/* +static u32int +get32(uchar *p) +{ + return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3]; +} +*/ + +static int +get16(uchar *p) +{ + return (p[0]<<8)|p[1]; +} + +static char *charmap = "0123456789abcdef"; + +static void +pdump(int len, void *a, char *tag) +{ + uchar *p; + int i; + char buf[65+32]; + char *q; + + p = a; + strcpy(buf, tag); + while(len > 0){ + q = buf + strlen(tag); + for(i = 0; len > 0 && i < 32; i++){ + if(*p >= ' ' && *p < 0x7f){ + *q++ = ' '; + *q++ = *p; + } else { + *q++ = charmap[*p>>4]; + *q++ = charmap[*p & 0xf]; + } + len--; + p++; + } + *q = 0; + + if(len > 0) + pprint("%s...\n", buf); + else + pprint("%s\n", buf); + } +} diff --git a/sys/src/cmd/unix/drawterm/kern/error.c b/sys/src/cmd/unix/drawterm/kern/error.c new file mode 100755 index 000000000..465de475a --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/error.c @@ -0,0 +1,50 @@ +char Enoerror[] = "no error"; +char Emount[] = "inconsistent mount"; +char Eunmount[] = "not mounted"; +char Eunion[] = "not in union"; +char Emountrpc[] = "mount rpc error"; +char Eshutdown[] = "device shut down"; +char Enocreate[] = "mounted directory forbids creation"; +char Enonexist[] = "file does not exist"; +char Eexist[] = "file already exists"; +char Ebadsharp[] = "unknown device in # filename"; +char Enotdir[] = "not a directory"; +char Eisdir[] = "file is a directory"; +char Ebadchar[] = "bad character in file name"; +char Efilename[] = "file name syntax"; +char Eperm[] = "permission denied"; +char Ebadusefd[] = "inappropriate use of fd"; +char Ebadarg[] = "bad arg in system call"; +char Einuse[] = "device or object already in use"; +char Eio[] = "i/o error"; +char Etoobig[] = "read or write too large"; +char Etoosmall[] = "read or write too small"; +char Enoport[] = "network port not available"; +char Ehungup[] = "i/o on hungup channel"; +char Ebadctl[] = "bad process or channel control request"; +char Enodev[] = "no free devices"; +char Eprocdied[] = "process exited"; +char Enochild[] = "no living children"; +char Eioload[] = "i/o error in demand load"; +char Enovmem[] = "virtual memory allocation failed"; +char Ebadfd[] = "fd out of range or not open"; +char Enofd[] = "no free file descriptors"; +char Eisstream[] = "seek on a stream"; +char Ebadexec[] = "exec header invalid"; +char Etimedout[] = "connection timed out"; +char Econrefused[] = "connection refused"; +char Econinuse[] = "connection in use"; +char Eintr[] = "interrupted"; +char Enomem[] = "kernel allocate failed"; +char Enoswap[] = "swap space full"; +char Esoverlap[] = "segments overlap"; +char Emouseset[] = "mouse type already set"; +char Eshort[] = "i/o count too small"; +char Egreg[] = "ken has left the building"; +char Ebadspec[] = "bad attach specifier"; +char Enoreg[] = "process has no saved registers"; +char Enoattach[] = "mount/attach disallowed"; +char Eshortstat[] = "stat buffer too small"; +char Ebadstat[] = "malformed stat buffer"; +char Enegoff[] = "negative i/o offset"; +char Ecmdargs[] = "wrong #args in control message"; diff --git a/sys/src/cmd/unix/drawterm/kern/error.h b/sys/src/cmd/unix/drawterm/kern/error.h new file mode 100755 index 000000000..6bb87622e --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/error.h @@ -0,0 +1,50 @@ +extern char Enoerror[]; /* no error */ +extern char Emount[]; /* inconsistent mount */ +extern char Eunmount[]; /* not mounted */ +extern char Eunion[]; /* not in union */ +extern char Emountrpc[]; /* mount rpc error */ +extern char Eshutdown[]; /* device shut down */ +extern char Enocreate[]; /* mounted directory forbids creation */ +extern char Enonexist[]; /* file does not exist */ +extern char Eexist[]; /* file already exists */ +extern char Ebadsharp[]; /* unknown device in # filename */ +extern char Enotdir[]; /* not a directory */ +extern char Eisdir[]; /* file is a directory */ +extern char Ebadchar[]; /* bad character in file name */ +extern char Efilename[]; /* file name syntax */ +extern char Eperm[]; /* permission denied */ +extern char Ebadusefd[]; /* inappropriate use of fd */ +extern char Ebadarg[]; /* bad arg in system call */ +extern char Einuse[]; /* device or object already in use */ +extern char Eio[]; /* i/o error */ +extern char Etoobig[]; /* read or write too large */ +extern char Etoosmall[]; /* read or write too small */ +extern char Enoport[]; /* network port not available */ +extern char Ehungup[]; /* i/o on hungup channel */ +extern char Ebadctl[]; /* bad process or channel control request */ +extern char Enodev[]; /* no free devices */ +extern char Eprocdied[]; /* process exited */ +extern char Enochild[]; /* no living children */ +extern char Eioload[]; /* i/o error in demand load */ +extern char Enovmem[]; /* virtual memory allocation failed */ +extern char Ebadfd[]; /* fd out of range or not open */ +extern char Enofd[]; /* no free file descriptors */ +extern char Eisstream[]; /* seek on a stream */ +extern char Ebadexec[]; /* exec header invalid */ +extern char Etimedout[]; /* connection timed out */ +extern char Econrefused[]; /* connection refused */ +extern char Econinuse[]; /* connection in use */ +extern char Eintr[]; /* interrupted */ +extern char Enomem[]; /* kernel allocate failed */ +extern char Enoswap[]; /* swap space full */ +extern char Esoverlap[]; /* segments overlap */ +extern char Emouseset[]; /* mouse type already set */ +extern char Eshort[]; /* i/o count too small */ +extern char Egreg[]; /* ken has left the building */ +extern char Ebadspec[]; /* bad attach specifier */ +extern char Enoreg[]; /* process has no saved registers */ +extern char Enoattach[]; /* mount/attach disallowed */ +extern char Eshortstat[]; /* stat buffer too small */ +extern char Ebadstat[]; /* malformed stat buffer */ +extern char Enegoff[]; /* negative i/o offset */ +extern char Ecmdargs[]; /* wrong #args in control message */ diff --git a/sys/src/cmd/unix/drawterm/kern/exportfs.c b/sys/src/cmd/unix/drawterm/kern/exportfs.c new file mode 100755 index 000000000..46cb90d0c --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/exportfs.c @@ -0,0 +1,821 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +typedef struct Fid Fid; +typedef struct Export Export; +typedef struct Exq Exq; + +#define nil ((void*)0) + +enum +{ + Nfidhash = 1, + MAXRPC = MAXMSG+MAXFDATA, + MAXDIRREAD = (MAXFDATA/DIRLEN)*DIRLEN +}; + +struct Export +{ + Ref r; + Exq* work; + Lock fidlock; + Fid* fid[Nfidhash]; + Chan* root; + Chan* io; + Pgrp* pgrp; + int npart; + char part[MAXRPC]; +}; + +struct Fid +{ + Fid* next; + Fid** last; + Chan* chan; + long offset; + int fid; + int ref; /* fcalls using the fid; locked by Export.Lock */ + int attached; /* fid attached or cloned but not clunked */ +}; + +struct Exq +{ + Lock lk; + int nointr; + int noresponse; /* don't respond to this one */ + Exq* next; + int shut; /* has been noted for shutdown */ + Export* export; + void* slave; + Fcall rpc; + char buf[MAXRPC]; +}; + +struct +{ + Lock l; + Qlock qwait; + Rendez rwait; + Exq *head; /* work waiting for a slave */ + Exq *tail; +}exq; + +static void exshutdown(Export*); +static void exflush(Export*, int, int); +static void exslave(void*); +static void exfree(Export*); +static void exportproc(Export*); + +static char* Exauth(Export*, Fcall*); +static char* Exattach(Export*, Fcall*); +static char* Exclunk(Export*, Fcall*); +static char* Excreate(Export*, Fcall*); +static char* Exopen(Export*, Fcall*); +static char* Exread(Export*, Fcall*); +static char* Exremove(Export*, Fcall*); +static char* Exstat(Export*, Fcall*); +static char* Exwalk(Export*, Fcall*); +static char* Exwrite(Export*, Fcall*); +static char* Exwstat(Export*, Fcall*); +static char* Exversion(Export*, Fcall*); + +static char *(*fcalls[Tmax])(Export*, Fcall*); + +static char Enofid[] = "no such fid"; +static char Eseekdir[] = "can't seek on a directory"; +static char Ereaddir[] = "unaligned read of a directory"; +static int exdebug = 0; + +int +sysexport(int fd) +{ + Chan *c; + Export *fs; + + if(waserror()) + return -1; + + c = fdtochan(fd, ORDWR, 1, 1); + poperror(); + c->flag |= CMSG; + + fs = mallocz(sizeof(Export)); + fs->r.ref = 1; + fs->pgrp = up->pgrp; + refinc(&fs->pgrp->r); + refinc(&up->slash->r); + fs->root = up->slash; + refinc(&fs->root->r); + fs->root = domount(fs->root); + fs->io = c; + + exportproc(fs); + + return 0; +} + +static void +exportinit(void) +{ + lock(&exq.l); + if(fcalls[Tversion] != nil) { + unlock(&exq.l); + return; + } + + fmtinstall('F', fcallfmt); + fmtinstall('D', dirfmt); + fmtinstall('M', dirmodefmt); + fcalls[Tversion] = Exversion; + fcalls[Tauth] = Exauth; + fcalls[Tattach] = Exattach; + fcalls[Twalk] = Exwalk; + fcalls[Topen] = Exopen; + fcalls[Tcreate] = Excreate; + fcalls[Tread] = Exread; + fcalls[Twrite] = Exwrite; + fcalls[Tclunk] = Exclunk; + fcalls[Tremove] = Exremove; + fcalls[Tstat] = Exstat; + fcalls[Twstat] = Exwstat; + unlock(&exq.l); +} + +void +exportproc(Export *fs) +{ + Exq *q; + char *buf; + int n, cn, len; + + exportinit(); + + for(;;){ + q = mallocz(sizeof(Exq)); + if(q == 0) + panic("no memory"); + + q->rpc.data = q->buf + MAXMSG; + + buf = q->buf; + len = MAXRPC; + if(fs->npart) { + memmove(buf, fs->part, fs->npart); + buf += fs->npart; + len -= fs->npart; + goto chk; + } + for(;;) { + if(waserror()) + goto bad; + + n = (*devtab[fs->io->type].read)(fs->io, buf, len, 0); + poperror(); + + if(n <= 0) + goto bad; + + buf += n; + len -= n; + chk: + n = buf - q->buf; + + /* convM2S returns size of correctly decoded message */ + cn = convM2S(q->buf, &q->rpc, n); + if(cn < 0){ + iprint("bad message type in devmnt\n"); + goto bad; + } + if(cn > 0) { + n -= cn; + if(n < 0){ + iprint("negative size in devmnt"); + goto bad; + } + fs->npart = n; + if(n != 0) + memmove(fs->part, q->buf+cn, n); + break; + } + } + if(exdebug) + iprint("export %d <- %F\n", getpid(), &q->rpc); + + if(q->rpc.type == Tflush){ + exflush(fs, q->rpc.tag, q->rpc.oldtag); + free(q); + continue; + } + + q->export = fs; + refinc(&fs->r); + + lock(&exq.l); + if(exq.head == nil) + exq.head = q; + else + exq.tail->next = q; + q->next = nil; + exq.tail = q; + unlock(&exq.l); + if(exq.qwait.first == nil) { + n = thread("exportfs", exslave, nil); +/* iprint("launch export (pid=%ux)\n", n); */ + } + rendwakeup(&exq.rwait); + } +bad: + free(q); + exshutdown(fs); + exfree(fs); +} + +void +exflush(Export *fs, int flushtag, int tag) +{ + Exq *q, **last; + int n; + Fcall fc; + char buf[MAXMSG]; + + /* hasn't been started? */ + lock(&exq.l); + last = &exq.head; + for(q = exq.head; q != nil; q = q->next){ + if(q->export == fs && q->rpc.tag == tag){ + *last = q->next; + unlock(&exq.l); + exfree(fs); + free(q); + goto Respond; + } + last = &q->next; + } + unlock(&exq.l); + + /* in progress? */ + lock(&fs->r.l); + for(q = fs->work; q != nil; q = q->next){ + if(q->rpc.tag == tag && !q->noresponse){ + lock(&q->lk); + q->noresponse = 1; + if(!q->nointr) + intr(q->slave); + unlock(&q->lk); + unlock(&fs->r.l); + goto Respond; + return; + } + } + unlock(&fs->r.l); + +if(exdebug) iprint("exflush: did not find rpc: %d\n", tag); + +Respond: + fc.type = Rflush; + fc.tag = flushtag; + n = convS2M(&fc, buf); +if(exdebug) iprint("exflush -> %F\n", &fc); + if(!waserror()){ + (*devtab[fs->io->type].write)(fs->io, buf, n, 0); + poperror(); + } +} + +void +exshutdown(Export *fs) +{ + Exq *q, **last; + + lock(&exq.l); + last = &exq.head; + for(q = exq.head; q != nil; q = *last){ + if(q->export == fs){ + *last = q->next; + exfree(fs); + free(q); + continue; + } + last = &q->next; + } + unlock(&exq.l); + + lock(&fs->r.l); + q = fs->work; + while(q != nil){ + if(q->shut){ + q = q->next; + continue; + } + q->shut = 1; + unlock(&fs->r.l); + /* postnote(q->slave, 1, "interrupted", NUser); */ + iprint("postnote 2!\n"); + lock(&fs->r.l); + q = fs->work; + } + unlock(&fs->r.l); +} + +void +exfree(Export *fs) +{ + Fid *f, *n; + int i; + + if(refdec(&fs->r) != 0) + return; + closepgrp(fs->pgrp); + cclose(fs->root); + cclose(fs->io); + for(i = 0; i < Nfidhash; i++){ + for(f = fs->fid[i]; f != nil; f = n){ + if(f->chan != nil) + cclose(f->chan); + n = f->next; + free(f); + } + } + free(fs); +} + +int +exwork(void *a) +{ + return exq.head != nil; +} + +void +exslave(void *a) +{ + Export *fs; + Exq *q, *t, **last; + char *err; + int n; +/* + closepgrp(up->pgrp); + up->pgrp = nil; +*/ + for(;;){ + qlock(&exq.qwait); + rendsleep(&exq.rwait, exwork, nil); + + lock(&exq.l); + q = exq.head; + if(q == nil) { + unlock(&exq.l); + qunlock(&exq.qwait); + continue; + } + exq.head = q->next; + q->slave = curthread(); + unlock(&exq.l); + + qunlock(&exq.qwait); + + q->noresponse = 0; + q->nointr = 0; + fs = q->export; + lock(&fs->r.l); + q->next = fs->work; + fs->work = q; + unlock(&fs->r.l); + + up->pgrp = q->export->pgrp; + + if(exdebug > 1) + iprint("exslave dispatch %d %F\n", getpid(), &q->rpc); + + if(waserror()){ + iprint("exslave err %r\n"); + err = up->errstr; + goto Err; + } + if(q->rpc.type >= Tmax || !fcalls[q->rpc.type]) + err = "bad fcall type"; + else + err = (*fcalls[q->rpc.type])(fs, &q->rpc); + + poperror(); + Err:; + q->rpc.type++; + if(err){ + q->rpc.type = Rerror; + strncpy(q->rpc.ename, err, ERRLEN); + } + n = convS2M(&q->rpc, q->buf); + + if(exdebug) + iprint("exslve %d -> %F\n", getpid(), &q->rpc); + + lock(&q->lk); + if(q->noresponse == 0){ + q->nointr = 1; + clearintr(); + if(!waserror()){ + (*devtab[fs->io->type].write)(fs->io, q->buf, n, 0); + poperror(); + } + } + unlock(&q->lk); + + /* + * exflush might set noresponse at this point, but + * setting noresponse means don't send a response now; + * it's okay that we sent a response already. + */ + if(exdebug > 1) + iprint("exslave %d written %d\n", getpid(), q->rpc.tag); + + lock(&fs->r.l); + last = &fs->work; + for(t = fs->work; t != nil; t = t->next){ + if(t == q){ + *last = q->next; + break; + } + last = &t->next; + } + unlock(&fs->r.l); + + exfree(q->export); + free(q); + } + iprint("exslave shut down"); + threadexit(); +} + +Fid* +Exmkfid(Export *fs, int fid) +{ + ulong h; + Fid *f, *nf; + + nf = mallocz(sizeof(Fid)); + if(nf == nil) + return nil; + lock(&fs->fidlock); + h = fid % Nfidhash; + for(f = fs->fid[h]; f != nil; f = f->next){ + if(f->fid == fid){ + unlock(&fs->fidlock); + free(nf); + return nil; + } + } + + nf->next = fs->fid[h]; + if(nf->next != nil) + nf->next->last = &nf->next; + nf->last = &fs->fid[h]; + fs->fid[h] = nf; + + nf->fid = fid; + nf->ref = 1; + nf->attached = 1; + nf->offset = 0; + nf->chan = nil; + unlock(&fs->fidlock); + return nf; +} + +Fid* +Exgetfid(Export *fs, int fid) +{ + Fid *f; + ulong h; + + lock(&fs->fidlock); + h = fid % Nfidhash; + for(f = fs->fid[h]; f; f = f->next) { + if(f->fid == fid){ + if(f->attached == 0) + break; + f->ref++; + unlock(&fs->fidlock); + return f; + } + } + unlock(&fs->fidlock); + return nil; +} + +void +Exputfid(Export *fs, Fid *f) +{ + lock(&fs->fidlock); + f->ref--; + if(f->ref == 0 && f->attached == 0){ + if(f->chan != nil) + cclose(f->chan); + f->chan = nil; + *f->last = f->next; + if(f->next != nil) + f->next->last = f->last; + unlock(&fs->fidlock); + free(f); + return; + } + unlock(&fs->fidlock); +} + +char* +Exsession(Export *e, Fcall *rpc) +{ + memset(rpc->authid, 0, sizeof(rpc->authid)); + memset(rpc->authdom, 0, sizeof(rpc->authdom)); + memset(rpc->chal, 0, sizeof(rpc->chal)); + return nil; +} + +char* +Exauth(Export *e, Fcall *f) +{ + return "authentication not required"; +} + +char* +Exattach(Export *fs, Fcall *rpc) +{ + Fid *f; + + f = Exmkfid(fs, rpc->fid); + if(f == nil) + return Einuse; + if(waserror()){ + f->attached = 0; + Exputfid(fs, f); + return up->errstr; + } + f->chan = clone(fs->root, nil); + poperror(); + rpc->qid = f->chan->qid; + Exputfid(fs, f); + return nil; +} + +char* +Exclone(Export *fs, Fcall *rpc) +{ + Fid *f, *nf; + + if(rpc->fid == rpc->newfid) + return Einuse; + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + nf = Exmkfid(fs, rpc->newfid); + if(nf == nil){ + Exputfid(fs, f); + return Einuse; + } + if(waserror()){ + Exputfid(fs, f); + Exputfid(fs, nf); + return up->errstr; + } + nf->chan = clone(f->chan, nil); + poperror(); + Exputfid(fs, f); + Exputfid(fs, nf); + return nil; +} + +char* +Exclunk(Export *fs, Fcall *rpc) +{ + Fid *f; + + f = Exgetfid(fs, rpc->fid); + if(f != nil){ + f->attached = 0; + Exputfid(fs, f); + } + return nil; +} + +char* +Exwalk(Export *fs, Fcall *rpc) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->errstr; + } + c = walk(f->chan, rpc->name, 1); + if(c == nil) + error(Enonexist); + poperror(); + + f->chan = c; + rpc->qid = c->qid; + Exputfid(fs, f); + return nil; +} + +char* +Exopen(Export *fs, Fcall *rpc) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->errstr; + } + c = f->chan; + c = (*devtab[c->type].open)(c, rpc->mode); + poperror(); + + f->chan = c; + f->offset = 0; + rpc->qid = f->chan->qid; + Exputfid(fs, f); + return nil; +} + +char* +Excreate(Export *fs, Fcall *rpc) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->errstr; + } + c = f->chan; + if(c->mnt && !(c->flag&CCREATE)) + c = createdir(c); + (*devtab[c->type].create)(c, rpc->name, rpc->mode, rpc->perm); + poperror(); + + f->chan = c; + rpc->qid = f->chan->qid; + Exputfid(fs, f); + return nil; +} + +char* +Exread(Export *fs, Fcall *rpc) +{ + Fid *f; + Chan *c; + long off; + int dir, n, seek; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + + c = f->chan; + dir = c->qid.path & CHDIR; + if(dir){ + rpc->count -= rpc->count%DIRLEN; + if(rpc->offset%DIRLEN || rpc->count==0){ + Exputfid(fs, f); + return Ereaddir; + } + if(f->offset > rpc->offset){ + Exputfid(fs, f); + return Eseekdir; + } + } + + if(waserror()) { + Exputfid(fs, f); + return up->errstr; + } + + for(;;){ + n = rpc->count; + seek = 0; + off = rpc->offset; + if(dir && f->offset != off){ + off = f->offset; + n = rpc->offset - off; + if(n > MAXDIRREAD) + n = MAXDIRREAD; + seek = 1; + } + if(dir && c->mnt != nil) + n = unionread(c, rpc->data, n); + else{ + c->offset = off; + n = (*devtab[c->type].read)(c, rpc->data, n, off); + } + if(n == 0 || !seek) + break; + f->offset = off + n; + c->offset += n; + } + rpc->count = n; + poperror(); + Exputfid(fs, f); + return nil; +} + +char* +Exwrite(Export *fs, Fcall *rpc) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->errstr; + } + c = f->chan; + if(c->qid.path & CHDIR) + error(Eisdir); + rpc->count = (*devtab[c->type].write)(c, rpc->data, rpc->count, rpc->offset); + poperror(); + Exputfid(fs, f); + return nil; +} + +char* +Exstat(Export *fs, Fcall *rpc) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->errstr; + } + c = f->chan; + (*devtab[c->type].stat)(c, rpc->stat); + poperror(); + Exputfid(fs, f); + return nil; +} + +char* +Exwstat(Export *fs, Fcall *rpc) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->errstr; + } + c = f->chan; + (*devtab[c->type].wstat)(c, rpc->stat); + poperror(); + Exputfid(fs, f); + return nil; +} + +char* +Exremove(Export *fs, Fcall *rpc) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->errstr; + } + c = f->chan; + (*devtab[c->type].remove)(c); + poperror(); + + /* + * chan is already clunked by remove. + * however, we need to recover the chan, + * and follow sysremove's lead in making to point to root. + */ + c->type = 0; + + f->attached = 0; + Exputfid(fs, f); + return nil; +} diff --git a/sys/src/cmd/unix/drawterm/kern/fns.h b/sys/src/cmd/unix/drawterm/kern/fns.h new file mode 100755 index 000000000..97a935941 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/fns.h @@ -0,0 +1,392 @@ +#define ROUND(s, sz) (((s)+((sz)-1))&~((sz)-1)) + +void accounttime(void); +void addclock0link(void (*)(void), int); +int addphysseg(Physseg*); +void addbootfile(char*, uchar*, ulong); +Block* adjustblock(Block*, int); +void alarmkproc(void*); +Block* allocb(int); +int anyhigher(void); +int anyready(void); +Page* auxpage(void); +Block* bl2mem(uchar*, Block*, int); +int blocklen(Block*); +void callwithureg(void(*)(Ureg*)); +char* c2name(Chan*); +int cangetc(void*); +int canlock(Lock*); +int canpage(Proc*); +int canputc(void*); +int canqlock(QLock*); +int canrlock(RWlock*); +void chandevinit(void); +void chandevreset(void); +void chandevshutdown(void); +void chanfree(Chan*); +void chanrec(Mnt*); +void checkalarms(void); +void checkb(Block*, char*); +void cinit(void); +Chan* cclone(Chan*); +void cclose(Chan*); +char* clipread(void); +int clipwrite(char*); +void closeegrp(Egrp*); +void closefgrp(Fgrp*); +void closemount(Mount*); +void closepgrp(Pgrp*); +void closergrp(Rgrp*); +long clrfpintr(void); +void cmderror(Cmdbuf*, char*); +int cmount(Chan**, Chan*, int, char*); +void cnameclose(Cname*); +void confinit(void); +void confinit1(int); +int consactive(void); +extern void (*consdebug)(void); +void copen(Chan*); +Block* concatblock(Block*); +Block* copyblock(Block*, int); +void copypage(Page*, Page*); +int cread(Chan*, uchar*, int, vlong); +void cunmount(Chan*, Chan*); +void cupdate(Chan*, uchar*, int, vlong); +void cwrite(Chan*, uchar*, int, vlong); +ulong dbgpc(Proc*); +int decref(Ref*); +int decrypt(void*, void*, int); +void delay(int); +Chan* devattach(int, char*); +Block* devbread(Chan*, long, ulong); +long devbwrite(Chan*, Block*, ulong); +Chan* devclone(Chan*); +int devconfig(int, char *, DevConf *); +void devcreate(Chan*, char*, int, ulong); +void devdir(Chan*, Qid, char*, vlong, char*, long, Dir*); +long devdirread(Chan*, char*, long, Dirtab*, int, Devgen*); +Devgen devgen; +void devinit(void); +int devno(int, int); +Chan* devopen(Chan*, int, Dirtab*, int, Devgen*); +void devpermcheck(char*, ulong, int); +void devpower(int); +void devremove(Chan*); +void devreset(void); +void devshutdown(void); +int devstat(Chan*, uchar*, int, Dirtab*, int, Devgen*); +Walkqid* devwalk(Chan*, Chan*, char**, int, Dirtab*, int, Devgen*); +int devwstat(Chan*, uchar*, int); +void drawactive(int); +void drawcmap(void); +int drawcanqlock(void); +void drawqlock(void); +void drawqunlock(void); +void dumpaproc(Proc*); +void dumpqueues(void); +void dumpregs(Ureg*); +void dumpstack(void); +Fgrp* dupfgrp(Fgrp*); +void duppage(Page*); +void dupswap(Page*); +int emptystr(char*); +int encrypt(void*, void*, int); +void envcpy(Egrp*, Egrp*); +int eqchan(Chan*, Chan*, int); +int eqqid(Qid, Qid); +void error(char*); +long execregs(ulong, ulong, ulong); +void exhausted(char*); +void exit(int); +uvlong fastticks(uvlong*); +int fault(ulong, int); +void fdclose(int, int); +Chan* fdtochan(int, int, int, int); +int fixfault(Segment*, ulong, int, int); +void flushmmu(void); +void forkchild(Proc*, Ureg*); +void forkret(void); +void free(void*); +void freeb(Block*); +void freeblist(Block*); +int freebroken(void); +void freepte(Segment*, Pte*); +void freesegs(int); +void freesession(Session*); +ulong getmalloctag(void*); +ulong getrealloctag(void*); +void gotolabel(Label*); +char* getconfenv(void); +int haswaitq(void*); +long hostdomainwrite(char*, int); +long hostownerwrite(char*, int); +void hzsched(void); +void iallocinit(void); +Block* iallocb(int); +void iallocsummary(void); +long ibrk(ulong, int); +void ilock(Lock*); +void iunlock(Lock*); +int incref(Ref*); +void initseg(void); +int iprint(char*, ...); +void isdir(Chan*); +int iseve(void); +#define islo() (0) +Segment* isoverlap(Proc*, ulong, int); +int ispages(void*); +int isphysseg(char*); +void ixsummary(void); +void kbdclock(void); +int kbdcr2nl(Queue*, int); +int kbdputc(Queue*, int); +void kbdrepeat(int); +long keyread(char*, int, long); +void kickpager(void); +void killbig(void); +int kproc(char*, void(*)(void*), void*); +void kprocchild(Proc*, void (*)(void*), void*); +extern void (*kproftimer)(ulong); +void ksetenv(char*, char*, int); +void kstrcpy(char*, char*, int); +void kstrdup(char**, char*); +long latin1(Rune*, int); +void lock(Lock*); +void lockinit(void); +void logopen(Log*); +void logclose(Log*); +char* logctl(Log*, int, char**, Logflag*); +void logn(Log*, int, void*, int); +long logread(Log*, void*, ulong, long); +void log(Log*, int, char*, ...); +Cmdtab* lookupcmd(Cmdbuf*, Cmdtab*, int); +void machinit(void); +void* mallocz(ulong, int); +#define malloc kmalloc +void* malloc(ulong); +void mallocsummary(void); +Block* mem2bl(uchar*, int); +void mfreeseg(Segment*, ulong, int); +void microdelay(int); +void mkqid(Qid*, vlong, ulong, int); +void mmurelease(Proc*); +void mmuswitch(Proc*); +Chan* mntauth(Chan*, char*); +void mntdump(void); +long mntversion(Chan*, char*, int, int); +void mountfree(Mount*); +ulong ms2tk(ulong); +ulong msize(void*); +ulong ms2tk(ulong); +uvlong ms2fastticks(ulong); +void muxclose(Mnt*); +Chan* namec(char*, int, int, ulong); +Chan* newchan(void); +int newfd(Chan*); +Mhead* newmhead(Chan*); +Mount* newmount(Mhead*, Chan*, int, char*); +Page* newpage(int, Segment **, ulong); +Pgrp* newpgrp(void); +Rgrp* newrgrp(void); +Proc* newproc(void); +char* nextelem(char*, char*); +void nexterror(void); +Cname* newcname(char*); +int notify(Ureg*); +int nrand(int); +int okaddr(ulong, ulong, int); +int openmode(ulong); +void oserrstr(void); +void oserror(void); +Block* packblock(Block*); +Block* padblock(Block*, int); +void pagechainhead(Page*); +void pageinit(void); +void pagersummary(void); +void panic(char*, ...); +Cmdbuf* parsecmd(char *a, int n); +ulong perfticks(void); +void pexit(char*, int); +int preempted(void); +void printinit(void); +int procindex(ulong); +void pgrpcpy(Pgrp*, Pgrp*); +void pgrpnote(ulong, char*, long, int); +Pgrp* pgrptab(int); +void pio(Segment *, ulong, ulong, Page **); +#define poperror() up->nerrlab-- +void portclock(Ureg*); +int postnote(Proc*, int, char*, int); +int pprint(char*, ...); +void prflush(void); +ulong procalarm(ulong); +int proccounter(char *name); +void procctl(Proc*); +void procdump(void); +int procfdprint(Chan*, int, int, char*, int); +void procinit0(void); +void procflushseg(Segment*); +void procpriority(Proc*, int, int); +Proc* proctab(int); +void procwired(Proc*, int); +Pte* ptealloc(void); +Pte* ptecpy(Pte*); +int pullblock(Block**, int); +Block* pullupblock(Block*, int); +Block* pullupqueue(Queue*, int); +void putmhead(Mhead*); +void putmmu(ulong, ulong, Page*); +void putpage(Page*); +void putseg(Segment*); +void putstr(char*); +void putstrn(char*, int); +void putswap(Page*); +ulong pwait(Waitmsg*); +Label* pwaserror(void); +void qaddlist(Queue*, Block*); +Block* qbread(Queue*, int); +long qbwrite(Queue*, Block*); +Queue* qbypass(void (*)(void*, Block*), void*); +int qcanread(Queue*); +void qclose(Queue*); +int qconsume(Queue*, void*, int); +Block* qcopy(Queue*, int, ulong); +int qdiscard(Queue*, int); +void qflush(Queue*); +void qfree(Queue*); +int qfull(Queue*); +Block* qget(Queue*); +void qhangup(Queue*, char*); +int qisclosed(Queue*); +void qinit(void); +int qiwrite(Queue*, void*, int); +int qlen(Queue*); +void qlock(QLock*); +Queue* qopen(int, int, void (*)(void*), void*); +int qpass(Queue*, Block*); +int qpassnolim(Queue*, Block*); +int qproduce(Queue*, void*, int); +void qputback(Queue*, Block*); +long qread(Queue*, void*, int); +Block* qremove(Queue*); +void qreopen(Queue*); +void qsetlimit(Queue*, int); +void qunlock(QLock*); +int qwindow(Queue*); +int qwrite(Queue*, void*, int); +void qnoblock(Queue*, int); +int rand(void); +void randominit(void); +ulong randomread(void*, ulong); +void rdb(void); +int readnum(ulong, char*, ulong, ulong, int); +int readstr(ulong, char*, ulong, char*); +void ready(Proc*); +void rebootcmd(int, char**); +void reboot(void*, void*, ulong); +void relocateseg(Segment*, ulong); +void renameuser(char*, char*); +void resched(char*); +void resrcwait(char*); +int return0(void*); +void rlock(RWlock*); +long rtctime(void); +void runlock(RWlock*); +Proc* runproc(void); +void savefpregs(FPsave*); +extern void (*saveintrts)(void); +void sched(void); +void scheddump(void); +void schedinit(void); +extern void (*screenputs)(char*, int); +long seconds(void); +ulong segattach(Proc*, ulong, char *, ulong, ulong); +void segclock(ulong); +void segpage(Segment*, Page*); +void setkernur(Ureg*, Proc*); +int setlabel(Label*); +void setmalloctag(void*, uintptr); +void setrealloctag(void*, ulong); +void setregisters(Ureg*, char*, char*, int); +void setswapchan(Chan*); +long showfilewrite(char*, int); +char* skipslash(char*); +void sleep(Rendez*, int(*)(void*), void*); +void* smalloc(ulong); +int splhi(void); +int spllo(void); +void splx(int); +void splxpc(int); +char* srvname(Chan*); +int swapcount(ulong); +int swapfull(void); +void swapinit(void); +void timeradd(Timer*); +void timerdel(Timer*); +void timersinit(void); +void timerintr(Ureg*, uvlong); +void timerset(uvlong); +ulong tk2ms(ulong); +#define TK2MS(x) ((x)*(1000/HZ)) +vlong todget(vlong*); +void todfix(void); +void todsetfreq(vlong); +void todinit(void); +void todset(vlong, vlong, int); +Block* trimblock(Block*, int, int); +void tsleep(Rendez*, int (*)(void*), void*, int); +int uartctl(Uart*, char*); +int uartgetc(void); +void uartkick(void*); +void uartmouse(Uart*, int (*)(Queue*, int), int); +void uartputc(int); +void uartputs(char*, int); +void uartrecv(Uart*, char); +Uart* uartsetup(Uart*); +int uartstageoutput(Uart*); +void unbreak(Proc*); +void uncachepage(Page*); +long unionread(Chan*, void*, long); +void unlock(Lock*); +Proc** uploc(void); +void userinit(void); +ulong userpc(void); +long userwrite(char*, int); +#define validaddr(a, b, c) +void validname(char*, int); +void validstat(uchar*, int); +void vcacheinval(Page*, ulong); +void* vmemchr(void*, int, int); +Proc* wakeup(Rendez*); +int walk(Chan**, char**, int, int, int*); +#define waserror() (setjmp(pwaserror()->buf)) +void wlock(RWlock*); +void wunlock(RWlock*); +void* xalloc(ulong); +void* xallocz(ulong, int); +void xfree(void*); +void xhole(ulong, ulong); +void xinit(void); +int xmerge(void*, void*); +void* xspanalloc(ulong, int, ulong); +void xsummary(void); +void yield(void); +Segment* data2txt(Segment*); +Segment* dupseg(Segment**, int, int); +Segment* newseg(int, ulong, ulong); +Segment* seg(Proc*, ulong, int); +void hnputv(void*, vlong); +void hnputl(void*, ulong); +void hnputs(void*, ushort); +vlong nhgetv(void*); +ulong nhgetl(void*); +ushort nhgets(void*); +ulong ticks(void); +void osproc(Proc*); +void osnewproc(Proc*); +void procsleep(void); +void procwakeup(Proc*); +void osinit(void); +void screeninit(void); +extern void terminit(void); + diff --git a/sys/src/cmd/unix/drawterm/kern/netif.h b/sys/src/cmd/unix/drawterm/kern/netif.h new file mode 100755 index 000000000..06c42aec8 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/netif.h @@ -0,0 +1,133 @@ +typedef struct Etherpkt Etherpkt; +typedef struct Netaddr Netaddr; +typedef struct Netfile Netfile; +typedef struct Netif Netif; + +enum +{ + Nmaxaddr= 64, + Nmhash= 31, + + Ncloneqid= 1, + Naddrqid, + N2ndqid, + N3rdqid, + Ndataqid, + Nctlqid, + Nstatqid, + Ntypeqid, + Nifstatqid, +}; + +/* + * Macros to manage Qid's used for multiplexed devices + */ +#define NETTYPE(x) (((ulong)x)&0x1f) +#define NETID(x) ((((ulong)x))>>5) +#define NETQID(i,t) ((((ulong)i)<<5)|(t)) + +/* + * one per multiplexed connection + */ +struct Netfile +{ + QLock lk; + + int inuse; + ulong mode; + char owner[KNAMELEN]; + + int type; /* multiplexor type */ + int prom; /* promiscuous mode */ + int scan; /* base station scanning interval */ + int bridge; /* bridge mode */ + int headersonly; /* headers only - no data */ + uchar maddr[8]; /* bitmask of multicast addresses requested */ + int nmaddr; /* number of multicast addresses */ + + Queue *in; /* input buffer */ +}; + +/* + * a network address + */ +struct Netaddr +{ + Netaddr *next; /* allocation chain */ + Netaddr *hnext; + uchar addr[Nmaxaddr]; + int ref; +}; + +/* + * a network interface + */ +struct Netif +{ + QLock lk; + + /* multiplexing */ + char name[KNAMELEN]; /* for top level directory */ + int nfile; /* max number of Netfiles */ + Netfile **f; + + /* about net */ + int limit; /* flow control */ + int alen; /* address length */ + int mbps; /* megabits per sec */ + uchar addr[Nmaxaddr]; + uchar bcast[Nmaxaddr]; + Netaddr *maddr; /* known multicast addresses */ + int nmaddr; /* number of known multicast addresses */ + Netaddr *mhash[Nmhash]; /* hash table of multicast addresses */ + int prom; /* number of promiscuous opens */ + int scan; /* number of base station scanners */ + int all; /* number of -1 multiplexors */ + + /* statistics */ + int misses; + int inpackets; + int outpackets; + int crcs; /* input crc errors */ + int oerrs; /* output errors */ + int frames; /* framing errors */ + int overflows; /* packet overflows */ + int buffs; /* buffering errors */ + int soverflows; /* software overflow */ + + /* routines for touching the hardware */ + void *arg; + void (*promiscuous)(void*, int); + void (*multicast)(void*, uchar*, int); + void (*scanbs)(void*, uint); /* scan for base stations */ +}; + +void netifinit(Netif*, char*, int, ulong); +Walkqid* netifwalk(Netif*, Chan*, Chan*, char **, int); +Chan* netifopen(Netif*, Chan*, int); +void netifclose(Netif*, Chan*); +long netifread(Netif*, Chan*, void*, long, ulong); +Block* netifbread(Netif*, Chan*, long, ulong); +long netifwrite(Netif*, Chan*, void*, long); +int netifwstat(Netif*, Chan*, uchar*, int); +int netifstat(Netif*, Chan*, uchar*, int); +int activemulti(Netif*, uchar*, int); + +/* + * Ethernet specific + */ +enum +{ + Eaddrlen= 6, + ETHERMINTU = 60, /* minimum transmit size */ + ETHERMAXTU = 1514, /* maximum transmit size */ + ETHERHDRSIZE = 14, /* size of an ethernet header */ +}; + +struct Etherpkt +{ + uchar d[Eaddrlen]; + uchar s[Eaddrlen]; + uchar type[2]; + uchar data[1500]; +}; diff --git a/sys/src/cmd/unix/drawterm/kern/parse.c b/sys/src/cmd/unix/drawterm/kern/parse.c new file mode 100755 index 000000000..8c991f8dc --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/parse.c @@ -0,0 +1,113 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +/* + * Generous estimate of number of fields, including terminal nil pointer + */ +static int +ncmdfield(char *p, int n) +{ + int white, nwhite; + char *ep; + int nf; + + if(p == nil) + return 1; + + nf = 0; + ep = p+n; + white = 1; /* first text will start field */ + while(p < ep){ + nwhite = (strchr(" \t\r\n", *p++ & 0xFF) != 0); /* UTF is irrelevant */ + if(white && !nwhite) /* beginning of field */ + nf++; + white = nwhite; + } + return nf+1; /* +1 for nil */ +} + +/* + * parse a command written to a device + */ +Cmdbuf* +parsecmd(char *p, int n) +{ + Cmdbuf *volatile cb; + int nf; + char *sp; + + nf = ncmdfield(p, n); + + /* allocate Cmdbuf plus string pointers plus copy of string including \0 */ + sp = smalloc(sizeof(*cb) + nf * sizeof(char*) + n + 1); + cb = (Cmdbuf*)sp; + cb->f = (char**)(&cb[1]); + cb->buf = (char*)(&cb->f[nf]); + + if(up!=nil && waserror()){ + free(cb); + nexterror(); + } + memmove(cb->buf, p, n); + if(up != nil) + poperror(); + + /* dump new line and null terminate */ + if(n > 0 && cb->buf[n-1] == '\n') + n--; + cb->buf[n] = '\0'; + + cb->nf = tokenize(cb->buf, cb->f, nf-1); + cb->f[cb->nf] = nil; + + return cb; +} + +/* + * Reconstruct original message, for error diagnostic + */ +void +cmderror(Cmdbuf *cb, char *s) +{ + int i; + char *p, *e; + + p = up->genbuf; + e = p+ERRMAX-10; + p = seprint(p, e, "%s \"", s); + for(i=0; i<cb->nf; i++){ + if(i > 0) + p = seprint(p, e, " "); + p = seprint(p, e, "%q", cb->f[i]); + } + strcpy(p, "\""); + error(up->genbuf); +} + +/* + * Look up entry in table + */ +Cmdtab* +lookupcmd(Cmdbuf *cb, Cmdtab *ctab, int nctab) +{ + int i; + Cmdtab *ct; + + if(cb->nf == 0) + error("empty control message"); + + for(ct = ctab, i=0; i<nctab; i++, ct++){ + if(strcmp(ct->cmd, "*") !=0) /* wildcard always matches */ + if(strcmp(ct->cmd, cb->f[0]) != 0) + continue; + if(ct->narg != 0 && ct->narg != cb->nf) + cmderror(cb, Ecmdargs); + return ct; + } + + cmderror(cb, "unknown control message"); + return nil; +} diff --git a/sys/src/cmd/unix/drawterm/kern/pgrp.c b/sys/src/cmd/unix/drawterm/kern/pgrp.c new file mode 100755 index 000000000..30b1f3e7b --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/pgrp.c @@ -0,0 +1,272 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +static Ref pgrpid; +static Ref mountid; + +#ifdef NOTDEF +void +pgrpnote(ulong noteid, char *a, long n, int flag) +{ + Proc *p, *ep; + char buf[ERRMAX]; + + if(n >= ERRMAX-1) + error(Etoobig); + + memmove(buf, a, n); + buf[n] = 0; + p = proctab(0); + ep = p+conf.nproc; + for(; p < ep; p++) { + if(p->state == Dead) + continue; + if(up != p && p->noteid == noteid && p->kp == 0) { + qlock(&p->debug); + if(p->pid == 0 || p->noteid != noteid){ + qunlock(&p->debug); + continue; + } + if(!waserror()) { + postnote(p, 0, buf, flag); + poperror(); + } + qunlock(&p->debug); + } + } +} +#endif + +Pgrp* +newpgrp(void) +{ + Pgrp *p; + + p = smalloc(sizeof(Pgrp)); + p->ref.ref = 1; + p->pgrpid = incref(&pgrpid); + return p; +} + +Rgrp* +newrgrp(void) +{ + Rgrp *r; + + r = smalloc(sizeof(Rgrp)); + r->ref.ref = 1; + return r; +} + +void +closergrp(Rgrp *r) +{ + if(decref(&r->ref) == 0) + free(r); +} + +void +closepgrp(Pgrp *p) +{ + Mhead **h, **e, *f, *next; + + if(decref(&p->ref) != 0) + return; + + qlock(&p->debug); + wlock(&p->ns); + p->pgrpid = -1; + + e = &p->mnthash[MNTHASH]; + for(h = p->mnthash; h < e; h++) { + for(f = *h; f; f = next) { + wlock(&f->lock); + cclose(f->from); + mountfree(f->mount); + f->mount = nil; + next = f->hash; + wunlock(&f->lock); + putmhead(f); + } + } + wunlock(&p->ns); + qunlock(&p->debug); + free(p); +} + +void +pgrpinsert(Mount **order, Mount *m) +{ + Mount *f; + + m->order = 0; + if(*order == 0) { + *order = m; + return; + } + for(f = *order; f; f = f->order) { + if(m->mountid < f->mountid) { + m->order = f; + *order = m; + return; + } + order = &f->order; + } + *order = m; +} + +/* + * pgrpcpy MUST preserve the mountid allocation order of the parent group + */ +void +pgrpcpy(Pgrp *to, Pgrp *from) +{ + int i; + Mount *n, *m, **link, *order; + Mhead *f, **tom, **l, *mh; + + wlock(&from->ns); + order = 0; + tom = to->mnthash; + for(i = 0; i < MNTHASH; i++) { + l = tom++; + for(f = from->mnthash[i]; f; f = f->hash) { + rlock(&f->lock); + mh = newmhead(f->from); + *l = mh; + l = &mh->hash; + link = &mh->mount; + for(m = f->mount; m; m = m->next) { + n = newmount(mh, m->to, m->mflag, m->spec); + m->copy = n; + pgrpinsert(&order, m); + *link = n; + link = &n->next; + } + runlock(&f->lock); + } + } + /* + * Allocate mount ids in the same sequence as the parent group + */ + lock(&mountid.lk); + for(m = order; m; m = m->order) + m->copy->mountid = mountid.ref++; + unlock(&mountid.lk); + wunlock(&from->ns); +} + +Fgrp* +dupfgrp(Fgrp *f) +{ + Fgrp *new; + Chan *c; + int i; + + new = smalloc(sizeof(Fgrp)); + if(f == nil){ + new->fd = smalloc(DELTAFD*sizeof(Chan*)); + new->nfd = DELTAFD; + new->ref.ref = 1; + return new; + } + + lock(&f->ref.lk); + /* Make new fd list shorter if possible, preserving quantization */ + new->nfd = f->maxfd+1; + i = new->nfd%DELTAFD; + if(i != 0) + new->nfd += DELTAFD - i; + new->fd = malloc(new->nfd*sizeof(Chan*)); + if(new->fd == 0){ + unlock(&f->ref.lk); + error("no memory for fgrp"); + } + new->ref.ref = 1; + + new->maxfd = f->maxfd; + for(i = 0; i <= f->maxfd; i++) { + if((c = f->fd[i])){ + incref(&c->ref); + new->fd[i] = c; + } + } + unlock(&f->ref.lk); + + return new; +} + +void +closefgrp(Fgrp *f) +{ + int i; + Chan *c; + + if(f == 0) + return; + + if(decref(&f->ref) != 0) + return; + + for(i = 0; i <= f->maxfd; i++) + if((c = f->fd[i])) + cclose(c); + + free(f->fd); + free(f); +} + +Mount* +newmount(Mhead *mh, Chan *to, int flag, char *spec) +{ + Mount *m; + + m = smalloc(sizeof(Mount)); + m->to = to; + m->head = mh; + incref(&to->ref); + m->mountid = incref(&mountid); + m->mflag = flag; + if(spec != 0) + kstrdup(&m->spec, spec); + + return m; +} + +void +mountfree(Mount *m) +{ + Mount *f; + + while(m) { + f = m->next; + cclose(m->to); + m->mountid = 0; + free(m->spec); + free(m); + m = f; + } +} + +#ifdef NOTDEF +void +resrcwait(char *reason) +{ + char *p; + + if(up == 0) + panic("resrcwait"); + + p = up->psstate; + if(reason) { + up->psstate = reason; + print("%s\n", reason); + } + + tsleep(&up->sleep, return0, 0, 300); + up->psstate = p; +} +#endif diff --git a/sys/src/cmd/unix/drawterm/kern/posix.c b/sys/src/cmd/unix/drawterm/kern/posix.c new file mode 100755 index 000000000..069d6531f --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/posix.c @@ -0,0 +1,225 @@ +/* + * Posix generic OS implementation for drawterm. + */ + +#include "u.h" + +#ifndef _XOPEN_SOURCE /* for Apple and OpenBSD; not sure if needed */ +#define _XOPEN_SOURCE 500 +#endif + +#include <pthread.h> +#include <time.h> +#include <sys/time.h> +#include <sys/select.h> +#include <signal.h> +#include <pwd.h> +#include <errno.h> + +#include "lib.h" +#include "dat.h" +#include "fns.h" + +typedef struct Oproc Oproc; +struct Oproc +{ + int nsleep; + int nwakeup; + pthread_mutex_t mutex; + pthread_cond_t cond; +}; + +static pthread_key_t prdakey; + +Proc* +_getproc(void) +{ + void *v; + + if((v = pthread_getspecific(prdakey)) == nil) + panic("cannot getspecific"); + return v; +} + +void +_setproc(Proc *p) +{ + if(pthread_setspecific(prdakey, p) != 0) + panic("cannot setspecific"); +} + +void +osinit(void) +{ + if(pthread_key_create(&prdakey, 0)) + panic("cannot pthread_key_create"); +} + +#undef pipe +void +osnewproc(Proc *p) +{ + Oproc *op; + pthread_mutexattr_t attr; + + op = (Oproc*)p->oproc; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); + pthread_mutex_init(&op->mutex, &attr); + pthread_mutexattr_destroy(&attr); + pthread_cond_init(&op->cond, 0); +} + +void +osmsleep(int ms) +{ + struct timeval tv; + + tv.tv_sec = ms / 1000; + tv.tv_usec = (ms % 1000) * 1000; /* micro */ + if(select(0, NULL, NULL, NULL, &tv) < 0) + panic("select"); +} + +void +osyield(void) +{ + sched_yield(); +} + +void +oserrstr(void) +{ + char *p; + + if((p = strerror(errno)) != nil) + strecpy(up->errstr, up->errstr+ERRMAX, p); + else + snprint(up->errstr, ERRMAX, "unix error %d", errno); +} + +void +oserror(void) +{ + oserrstr(); + nexterror(); +} + +static void* tramp(void*); + +void +osproc(Proc *p) +{ + pthread_t pid; + + if(pthread_create(&pid, nil, tramp, p)){ + oserrstr(); + panic("osproc: %r"); + } + sched_yield(); +} + +static void* +tramp(void *vp) +{ + Proc *p; + + p = vp; + if(pthread_setspecific(prdakey, p)) + panic("cannot setspecific"); + (*p->fn)(p->arg); + /* BUG: leaks Proc */ + pthread_setspecific(prdakey, 0); + pthread_exit(0); + return 0; +} + +void +procsleep(void) +{ + Proc *p; + Oproc *op; + + p = up; + op = (Oproc*)p->oproc; + pthread_mutex_lock(&op->mutex); + op->nsleep++; + while(op->nsleep > op->nwakeup) + pthread_cond_wait(&op->cond, &op->mutex); + pthread_mutex_unlock(&op->mutex); +} + +void +procwakeup(Proc *p) +{ + Oproc *op; + + op = (Oproc*)p->oproc; + pthread_mutex_lock(&op->mutex); + op->nwakeup++; + if(op->nwakeup == op->nsleep) + pthread_cond_signal(&op->cond); + pthread_mutex_unlock(&op->mutex); +} + +int randfd; +#undef open +void +randominit(void) +{ +#ifdef USE_RANDOM + srandom(getpid()+fastticks(nil)+ticks()); +#else + if((randfd = open("/dev/urandom", OREAD)) < 0) + if((randfd = open("/dev/random", OREAD)) < 0) + panic("open /dev/random: %r"); +#endif +} + +#undef read +ulong +randomread(void *v, ulong n) +{ +#ifdef USE_RANDOM + int i; + + for(i=0; i<n; i++) + ((uchar*)v)[i] = random(); + return n; +#else + int m; + + if((m = read(randfd, v, n)) != n) + panic("short read from /dev/random: %d but %d", n, m); + return m; +#endif +} + +#undef time +long +seconds(void) +{ + return time(0); +} + +ulong +ticks(void) +{ + static long sec0 = 0, usec0; + struct timeval t; + + if(gettimeofday(&t, nil) < 0) + return 0; + if(sec0 == 0){ + sec0 = t.tv_sec; + usec0 = t.tv_usec; + } + return (t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000; +} + +long +showfilewrite(char *a, int n) +{ + error("not implemented"); + return -1; +} diff --git a/sys/src/cmd/unix/drawterm/kern/procinit.c b/sys/src/cmd/unix/drawterm/kern/procinit.c new file mode 100755 index 000000000..b2299d963 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/procinit.c @@ -0,0 +1,67 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +Rgrp *thergrp; + +void +procinit0(void) +{ + Proc *p; + + p = newproc(); + p->fgrp = dupfgrp(nil); + p->rgrp = newrgrp(); + p->pgrp = newpgrp(); + _setproc(p); + + up->slash = namec("#/", Atodir, 0, 0); + cnameclose(up->slash->name); + up->slash->name = newcname("/"); + up->dot = cclone(up->slash); +} + +Ref pidref; + +Proc* +newproc(void) +{ + Proc *p; + + p = mallocz(sizeof(Proc), 1); + p->pid = incref(&pidref); + strcpy(p->user, eve); + p->syserrstr = p->errbuf0; + p->errstr = p->errbuf1; + strcpy(p->text, "drawterm"); + osnewproc(p); + return p; +} + +int +kproc(char *name, void (*fn)(void*), void *arg) +{ + Proc *p; + + p = newproc(); + p->fn = fn; + p->arg = arg; + p->slash = cclone(up->slash); + p->dot = cclone(up->dot); + p->rgrp = up->rgrp; + if(p->rgrp) + incref(&p->rgrp->ref); + p->pgrp = up->pgrp; + if(up->pgrp) + incref(&up->pgrp->ref); + p->fgrp = up->fgrp; + if(p->fgrp) + incref(&p->fgrp->ref); + strecpy(p->text, p->text+sizeof p->text, name); + + osproc(p); + return p->pid; +} + diff --git a/sys/src/cmd/unix/drawterm/kern/qio.c b/sys/src/cmd/unix/drawterm/kern/qio.c new file mode 100755 index 000000000..edee200be --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/qio.c @@ -0,0 +1,1524 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +static ulong padblockcnt; +static ulong concatblockcnt; +static ulong pullupblockcnt; +static ulong copyblockcnt; +static ulong consumecnt; +static ulong producecnt; +static ulong qcopycnt; + +static int debugging; + +#define QDEBUG if(0) + +/* + * IO queues + */ +struct Queue +{ + Lock lk; + + Block* bfirst; /* buffer */ + Block* blast; + + int len; /* bytes allocated to queue */ + int dlen; /* data bytes in queue */ + int limit; /* max bytes in queue */ + int inilim; /* initial limit */ + int state; + int noblock; /* true if writes return immediately when q full */ + int eof; /* number of eofs read by user */ + + void (*kick)(void*); /* restart output */ + void (*bypass)(void*, Block*); /* bypass queue altogether */ + void* arg; /* argument to kick */ + + QLock rlock; /* mutex for reading processes */ + Rendez rr; /* process waiting to read */ + QLock wlock; /* mutex for writing processes */ + Rendez wr; /* process waiting to write */ + + char err[ERRMAX]; +}; + +enum +{ + Maxatomic = 64*1024, +}; + +uint qiomaxatomic = Maxatomic; + +void +ixsummary(void) +{ + debugging ^= 1; + iallocsummary(); + print("pad %lud, concat %lud, pullup %lud, copy %lud\n", + padblockcnt, concatblockcnt, pullupblockcnt, copyblockcnt); + print("consume %lud, produce %lud, qcopy %lud\n", + consumecnt, producecnt, qcopycnt); +} + +/* + * free a list of blocks + */ +void +freeblist(Block *b) +{ + Block *next; + + for(; b != 0; b = next){ + next = b->next; + b->next = 0; + freeb(b); + } +} + +/* + * pad a block to the front (or the back if size is negative) + */ +Block* +padblock(Block *bp, int size) +{ + int n; + Block *nbp; + + QDEBUG checkb(bp, "padblock 1"); + if(size >= 0){ + if(bp->rp - bp->base >= size){ + bp->rp -= size; + return bp; + } + + if(bp->next) + panic("padblock 0x%p", getcallerpc(&bp)); + n = BLEN(bp); + padblockcnt++; + nbp = allocb(size+n); + nbp->rp += size; + nbp->wp = nbp->rp; + memmove(nbp->wp, bp->rp, n); + nbp->wp += n; + freeb(bp); + nbp->rp -= size; + } else { + size = -size; + + if(bp->next) + panic("padblock 0x%p", getcallerpc(&bp)); + + if(bp->lim - bp->wp >= size) + return bp; + + n = BLEN(bp); + padblockcnt++; + nbp = allocb(size+n); + memmove(nbp->wp, bp->rp, n); + nbp->wp += n; + freeb(bp); + } + QDEBUG checkb(nbp, "padblock 1"); + return nbp; +} + +/* + * return count of bytes in a string of blocks + */ +int +blocklen(Block *bp) +{ + int len; + + len = 0; + while(bp) { + len += BLEN(bp); + bp = bp->next; + } + return len; +} + +/* + * return count of space in blocks + */ +int +blockalloclen(Block *bp) +{ + int len; + + len = 0; + while(bp) { + len += BALLOC(bp); + bp = bp->next; + } + return len; +} + +/* + * copy the string of blocks into + * a single block and free the string + */ +Block* +concatblock(Block *bp) +{ + int len; + Block *nb, *f; + + if(bp->next == 0) + return bp; + + nb = allocb(blocklen(bp)); + for(f = bp; f; f = f->next) { + len = BLEN(f); + memmove(nb->wp, f->rp, len); + nb->wp += len; + } + concatblockcnt += BLEN(nb); + freeblist(bp); + QDEBUG checkb(nb, "concatblock 1"); + return nb; +} + +/* + * make sure the first block has at least n bytes + */ +Block* +pullupblock(Block *bp, int n) +{ + int i; + Block *nbp; + + /* + * this should almost always be true, it's + * just to avoid every caller checking. + */ + if(BLEN(bp) >= n) + return bp; + + /* + * if not enough room in the first block, + * add another to the front of the list. + */ + if(bp->lim - bp->rp < n){ + nbp = allocb(n); + nbp->next = bp; + bp = nbp; + } + + /* + * copy bytes from the trailing blocks into the first + */ + n -= BLEN(bp); + while((nbp = bp->next)){ + i = BLEN(nbp); + if(i > n) { + memmove(bp->wp, nbp->rp, n); + pullupblockcnt++; + bp->wp += n; + nbp->rp += n; + QDEBUG checkb(bp, "pullupblock 1"); + return bp; + } else { + /* shouldn't happen but why crash if it does */ + if(i < 0){ + print("pullup negative length packet\n"); + i = 0; + } + memmove(bp->wp, nbp->rp, i); + pullupblockcnt++; + bp->wp += i; + bp->next = nbp->next; + nbp->next = 0; + freeb(nbp); + n -= i; + if(n == 0){ + QDEBUG checkb(bp, "pullupblock 2"); + return bp; + } + } + } + freeb(bp); + return 0; +} + +/* + * make sure the first block has at least n bytes + */ +Block* +pullupqueue(Queue *q, int n) +{ + Block *b; + + if(BLEN(q->bfirst) >= n) + return q->bfirst; + q->bfirst = pullupblock(q->bfirst, n); + for(b = q->bfirst; b != nil && b->next != nil; b = b->next) + ; + q->blast = b; + return q->bfirst; +} + +/* + * trim to len bytes starting at offset + */ +Block * +trimblock(Block *bp, int offset, int len) +{ + ulong l; + Block *nb, *startb; + + QDEBUG checkb(bp, "trimblock 1"); + if(blocklen(bp) < offset+len) { + freeblist(bp); + return nil; + } + + while((l = BLEN(bp)) < offset) { + offset -= l; + nb = bp->next; + bp->next = nil; + freeb(bp); + bp = nb; + } + + startb = bp; + bp->rp += offset; + + while((l = BLEN(bp)) < len) { + len -= l; + bp = bp->next; + } + + bp->wp -= (BLEN(bp) - len); + + if(bp->next) { + freeblist(bp->next); + bp->next = nil; + } + + return startb; +} + +/* + * copy 'count' bytes into a new block + */ +Block* +copyblock(Block *bp, int count) +{ + int l; + Block *nbp; + + QDEBUG checkb(bp, "copyblock 0"); + nbp = allocb(count); + for(; count > 0 && bp != 0; bp = bp->next){ + l = BLEN(bp); + if(l > count) + l = count; + memmove(nbp->wp, bp->rp, l); + nbp->wp += l; + count -= l; + } + if(count > 0){ + memset(nbp->wp, 0, count); + nbp->wp += count; + } + copyblockcnt++; + QDEBUG checkb(nbp, "copyblock 1"); + + return nbp; +} + +Block* +adjustblock(Block* bp, int len) +{ + int n; + Block *nbp; + + if(len < 0){ + freeb(bp); + return nil; + } + + if(bp->rp+len > bp->lim){ + nbp = copyblock(bp, len); + freeblist(bp); + QDEBUG checkb(nbp, "adjustblock 1"); + + return nbp; + } + + n = BLEN(bp); + if(len > n) + memset(bp->wp, 0, len-n); + bp->wp = bp->rp+len; + QDEBUG checkb(bp, "adjustblock 2"); + + return bp; +} + + +/* + * throw away up to count bytes from a + * list of blocks. Return count of bytes + * thrown away. + */ +int +pullblock(Block **bph, int count) +{ + Block *bp; + int n, bytes; + + bytes = 0; + if(bph == nil) + return 0; + + while(*bph != nil && count != 0) { + bp = *bph; + n = BLEN(bp); + if(count < n) + n = count; + bytes += n; + count -= n; + bp->rp += n; + QDEBUG checkb(bp, "pullblock "); + if(BLEN(bp) == 0) { + *bph = bp->next; + bp->next = nil; + freeb(bp); + } + } + return bytes; +} + +/* + * get next block from a queue, return null if nothing there + */ +Block* +qget(Queue *q) +{ + int dowakeup; + Block *b; + + /* sync with qwrite */ + ilock(&q->lk); + + b = q->bfirst; + if(b == nil){ + q->state |= Qstarve; + iunlock(&q->lk); + return nil; + } + q->bfirst = b->next; + b->next = 0; + q->len -= BALLOC(b); + q->dlen -= BLEN(b); + QDEBUG checkb(b, "qget"); + + /* if writer flow controlled, restart */ + if((q->state & Qflow) && q->len < q->limit/2){ + q->state &= ~Qflow; + dowakeup = 1; + } else + dowakeup = 0; + + iunlock(&q->lk); + + if(dowakeup) + wakeup(&q->wr); + + return b; +} + +/* + * throw away the next 'len' bytes in the queue + */ +int +qdiscard(Queue *q, int len) +{ + Block *b; + int dowakeup, n, sofar; + + ilock(&q->lk); + for(sofar = 0; sofar < len; sofar += n){ + b = q->bfirst; + if(b == nil) + break; + QDEBUG checkb(b, "qdiscard"); + n = BLEN(b); + if(n <= len - sofar){ + q->bfirst = b->next; + b->next = 0; + q->len -= BALLOC(b); + q->dlen -= BLEN(b); + freeb(b); + } else { + n = len - sofar; + b->rp += n; + q->dlen -= n; + } + } + + /* + * if writer flow controlled, restart + * + * This used to be + * q->len < q->limit/2 + * but it slows down tcp too much for certain write sizes. + * I really don't understand it completely. It may be + * due to the queue draining so fast that the transmission + * stalls waiting for the app to produce more data. - presotto + */ + if((q->state & Qflow) && q->len < q->limit){ + q->state &= ~Qflow; + dowakeup = 1; + } else + dowakeup = 0; + + iunlock(&q->lk); + + if(dowakeup) + wakeup(&q->wr); + + return sofar; +} + +/* + * Interrupt level copy out of a queue, return # bytes copied. + */ +int +qconsume(Queue *q, void *vp, int len) +{ + Block *b; + int n, dowakeup; + uchar *p = vp; + Block *tofree = nil; + + /* sync with qwrite */ + ilock(&q->lk); + + for(;;) { + b = q->bfirst; + if(b == 0){ + q->state |= Qstarve; + iunlock(&q->lk); + return -1; + } + QDEBUG checkb(b, "qconsume 1"); + + n = BLEN(b); + if(n > 0) + break; + q->bfirst = b->next; + q->len -= BALLOC(b); + + /* remember to free this */ + b->next = tofree; + tofree = b; + }; + + if(n < len) + len = n; + memmove(p, b->rp, len); + consumecnt += n; + b->rp += len; + q->dlen -= len; + + /* discard the block if we're done with it */ + if((q->state & Qmsg) || len == n){ + q->bfirst = b->next; + b->next = 0; + q->len -= BALLOC(b); + q->dlen -= BLEN(b); + + /* remember to free this */ + b->next = tofree; + tofree = b; + } + + /* if writer flow controlled, restart */ + if((q->state & Qflow) && q->len < q->limit/2){ + q->state &= ~Qflow; + dowakeup = 1; + } else + dowakeup = 0; + + iunlock(&q->lk); + + if(dowakeup) + wakeup(&q->wr); + + if(tofree != nil) + freeblist(tofree); + + return len; +} + +int +qpass(Queue *q, Block *b) +{ + int dlen, len, dowakeup; + + /* sync with qread */ + dowakeup = 0; + ilock(&q->lk); + if(q->len >= q->limit){ + freeblist(b); + iunlock(&q->lk); + return -1; + } + if(q->state & Qclosed){ + freeblist(b); + iunlock(&q->lk); + return BALLOC(b); + } + + /* add buffer to queue */ + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + len = BALLOC(b); + dlen = BLEN(b); + QDEBUG checkb(b, "qpass"); + while(b->next){ + b = b->next; + QDEBUG checkb(b, "qpass"); + len += BALLOC(b); + dlen += BLEN(b); + } + q->blast = b; + q->len += len; + q->dlen += dlen; + + if(q->len >= q->limit/2) + q->state |= Qflow; + + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + iunlock(&q->lk); + + if(dowakeup) + wakeup(&q->rr); + + return len; +} + +int +qpassnolim(Queue *q, Block *b) +{ + int dlen, len, dowakeup; + + /* sync with qread */ + dowakeup = 0; + ilock(&q->lk); + + if(q->state & Qclosed){ + freeblist(b); + iunlock(&q->lk); + return BALLOC(b); + } + + /* add buffer to queue */ + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + len = BALLOC(b); + dlen = BLEN(b); + QDEBUG checkb(b, "qpass"); + while(b->next){ + b = b->next; + QDEBUG checkb(b, "qpass"); + len += BALLOC(b); + dlen += BLEN(b); + } + q->blast = b; + q->len += len; + q->dlen += dlen; + + if(q->len >= q->limit/2) + q->state |= Qflow; + + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + iunlock(&q->lk); + + if(dowakeup) + wakeup(&q->rr); + + return len; +} + +/* + * if the allocated space is way out of line with the used + * space, reallocate to a smaller block + */ +Block* +packblock(Block *bp) +{ + Block **l, *nbp; + int n; + + for(l = &bp; *l; l = &(*l)->next){ + nbp = *l; + n = BLEN(nbp); + if((n<<2) < BALLOC(nbp)){ + *l = allocb(n); + memmove((*l)->wp, nbp->rp, n); + (*l)->wp += n; + (*l)->next = nbp->next; + freeb(nbp); + } + } + + return bp; +} + +int +qproduce(Queue *q, void *vp, int len) +{ + Block *b; + int dowakeup; + uchar *p = vp; + + /* sync with qread */ + dowakeup = 0; + ilock(&q->lk); + + /* no waiting receivers, room in buffer? */ + if(q->len >= q->limit){ + q->state |= Qflow; + iunlock(&q->lk); + return -1; + } + + /* save in buffer */ + b = iallocb(len); + if(b == 0){ + iunlock(&q->lk); + return 0; + } + memmove(b->wp, p, len); + producecnt += len; + b->wp += len; + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + q->blast = b; + /* b->next = 0; done by iallocb() */ + q->len += BALLOC(b); + q->dlen += BLEN(b); + QDEBUG checkb(b, "qproduce"); + + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + + if(q->len >= q->limit) + q->state |= Qflow; + iunlock(&q->lk); + + if(dowakeup) + wakeup(&q->rr); + + return len; +} + +/* + * copy from offset in the queue + */ +Block* +qcopy(Queue *q, int len, ulong offset) +{ + int sofar; + int n; + Block *b, *nb; + uchar *p; + + nb = allocb(len); + + ilock(&q->lk); + + /* go to offset */ + b = q->bfirst; + for(sofar = 0; ; sofar += n){ + if(b == nil){ + iunlock(&q->lk); + return nb; + } + n = BLEN(b); + if(sofar + n > offset){ + p = b->rp + offset - sofar; + n -= offset - sofar; + break; + } + QDEBUG checkb(b, "qcopy"); + b = b->next; + } + + /* copy bytes from there */ + for(sofar = 0; sofar < len;){ + if(n > len - sofar) + n = len - sofar; + memmove(nb->wp, p, n); + qcopycnt += n; + sofar += n; + nb->wp += n; + b = b->next; + if(b == nil) + break; + n = BLEN(b); + p = b->rp; + } + iunlock(&q->lk); + + return nb; +} + +/* + * called by non-interrupt code + */ +Queue* +qopen(int limit, int msg, void (*kick)(void*), void *arg) +{ + Queue *q; + + q = malloc(sizeof(Queue)); + if(q == 0) + return 0; + + q->limit = q->inilim = limit; + q->kick = kick; + q->arg = arg; + q->state = msg; + + q->state |= Qstarve; + q->eof = 0; + q->noblock = 0; + + return q; +} + +/* open a queue to be bypassed */ +Queue* +qbypass(void (*bypass)(void*, Block*), void *arg) +{ + Queue *q; + + q = malloc(sizeof(Queue)); + if(q == 0) + return 0; + + q->limit = 0; + q->arg = arg; + q->bypass = bypass; + q->state = 0; + + return q; +} + +static int +notempty(void *a) +{ + Queue *q = a; + + return (q->state & Qclosed) || q->bfirst != 0; +} + +/* + * wait for the queue to be non-empty or closed. + * called with q ilocked. + */ +static int +qwait(Queue *q) +{ + /* wait for data */ + for(;;){ + if(q->bfirst != nil) + break; + + if(q->state & Qclosed){ + if(++q->eof > 3) + return -1; + if(*q->err && strcmp(q->err, Ehungup) != 0) + return -1; + return 0; + } + + q->state |= Qstarve; /* flag requesting producer to wake me */ + iunlock(&q->lk); + sleep(&q->rr, notempty, q); + ilock(&q->lk); + } + return 1; +} + +/* + * add a block list to a queue + */ +void +qaddlist(Queue *q, Block *b) +{ + /* queue the block */ + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + q->len += blockalloclen(b); + q->dlen += blocklen(b); + while(b->next) + b = b->next; + q->blast = b; +} + +/* + * called with q ilocked + */ +Block* +qremove(Queue *q) +{ + Block *b; + + b = q->bfirst; + if(b == nil) + return nil; + q->bfirst = b->next; + b->next = nil; + q->dlen -= BLEN(b); + q->len -= BALLOC(b); + QDEBUG checkb(b, "qremove"); + return b; +} + +/* + * copy the contents of a string of blocks into + * memory. emptied blocks are freed. return + * pointer to first unconsumed block. + */ +Block* +bl2mem(uchar *p, Block *b, int n) +{ + int i; + Block *next; + + for(; b != nil; b = next){ + i = BLEN(b); + if(i > n){ + memmove(p, b->rp, n); + b->rp += n; + return b; + } + memmove(p, b->rp, i); + n -= i; + p += i; + b->rp += i; + next = b->next; + freeb(b); + } + return nil; +} + +/* + * copy the contents of memory into a string of blocks. + * return nil on error. + */ +Block* +mem2bl(uchar *p, int len) +{ + int n; + Block *b, *first, **l; + + first = nil; + l = &first; + if(waserror()){ + freeblist(first); + nexterror(); + } + do { + n = len; + if(n > Maxatomic) + n = Maxatomic; + + *l = b = allocb(n); + /* setmalloctag(b, (up->text[0]<<24)|(up->text[1]<<16)|(up->text[2]<<8)|up->text[3]); */ + memmove(b->wp, p, n); + b->wp += n; + p += n; + len -= n; + l = &b->next; + } while(len > 0); + poperror(); + + return first; +} + +/* + * put a block back to the front of the queue + * called with q ilocked + */ +void +qputback(Queue *q, Block *b) +{ + b->next = q->bfirst; + if(q->bfirst == nil) + q->blast = b; + q->bfirst = b; + q->len += BALLOC(b); + q->dlen += BLEN(b); +} + +/* + * flow control, get producer going again + * called with q ilocked + */ +static void +qwakeup_iunlock(Queue *q) +{ + int dowakeup = 0; + + /* if writer flow controlled, restart */ + if((q->state & Qflow) && q->len < q->limit/2){ + q->state &= ~Qflow; + dowakeup = 1; + } + + iunlock(&q->lk); + + /* wakeup flow controlled writers */ + if(dowakeup){ + if(q->kick) + q->kick(q->arg); + wakeup(&q->wr); + } +} + +/* + * get next block from a queue (up to a limit) + */ +Block* +qbread(Queue *q, int len) +{ + Block *b, *nb; + int n; + + qlock(&q->rlock); + if(waserror()){ + qunlock(&q->rlock); + nexterror(); + } + + ilock(&q->lk); + switch(qwait(q)){ + case 0: + /* queue closed */ + iunlock(&q->lk); + qunlock(&q->rlock); + poperror(); + return nil; + case -1: + /* multiple reads on a closed queue */ + iunlock(&q->lk); + error(q->err); + } + + /* if we get here, there's at least one block in the queue */ + b = qremove(q); + n = BLEN(b); + + /* split block if it's too big and this is not a message queue */ + nb = b; + if(n > len){ + if((q->state&Qmsg) == 0){ + n -= len; + b = allocb(n); + memmove(b->wp, nb->rp+len, n); + b->wp += n; + qputback(q, b); + } + nb->wp = nb->rp + len; + } + + /* restart producer */ + qwakeup_iunlock(q); + + poperror(); + qunlock(&q->rlock); + return nb; +} + +/* + * read a queue. if no data is queued, post a Block + * and wait on its Rendez. + */ +long +qread(Queue *q, void *vp, int len) +{ + Block *b, *first, **l; + int m, n; + + qlock(&q->rlock); + if(waserror()){ + qunlock(&q->rlock); + nexterror(); + } + + ilock(&q->lk); +again: + switch(qwait(q)){ + case 0: + /* queue closed */ + iunlock(&q->lk); + qunlock(&q->rlock); + poperror(); + return 0; + case -1: + /* multiple reads on a closed queue */ + iunlock(&q->lk); + error(q->err); + } + + /* if we get here, there's at least one block in the queue */ + if(q->state & Qcoalesce){ + /* when coalescing, 0 length blocks just go away */ + b = q->bfirst; + if(BLEN(b) <= 0){ + freeb(qremove(q)); + goto again; + } + + /* grab the first block plus as many + * following blocks as will completely + * fit in the read. + */ + n = 0; + l = &first; + m = BLEN(b); + for(;;) { + *l = qremove(q); + l = &b->next; + n += m; + + b = q->bfirst; + if(b == nil) + break; + m = BLEN(b); + if(n+m > len) + break; + } + } else { + first = qremove(q); + n = BLEN(first); + } + + /* copy to user space outside of the ilock */ + iunlock(&q->lk); + b = bl2mem(vp, first, len); + ilock(&q->lk); + + /* take care of any left over partial block */ + if(b != nil){ + n -= BLEN(b); + if(q->state & Qmsg) + freeb(b); + else + qputback(q, b); + } + + /* restart producer */ + qwakeup_iunlock(q); + + poperror(); + qunlock(&q->rlock); + return n; +} + +static int +qnotfull(void *a) +{ + Queue *q = a; + + return q->len < q->limit || (q->state & Qclosed); +} + +ulong noblockcnt; + +/* + * add a block to a queue obeying flow control + */ +long +qbwrite(Queue *q, Block *b) +{ + int n, dowakeup; + + n = BLEN(b); + + if(q->bypass){ + (*q->bypass)(q->arg, b); + return n; + } + + dowakeup = 0; + qlock(&q->wlock); + if(waserror()){ + if(b != nil) + freeb(b); + qunlock(&q->wlock); + nexterror(); + } + + ilock(&q->lk); + + /* give up if the queue is closed */ + if(q->state & Qclosed){ + iunlock(&q->lk); + error(q->err); + } + + /* if nonblocking, don't queue over the limit */ + if(q->len >= q->limit){ + if(q->noblock){ + iunlock(&q->lk); + freeb(b); + noblockcnt += n; + qunlock(&q->wlock); + poperror(); + return n; + } + } + + /* queue the block */ + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + q->blast = b; + b->next = 0; + q->len += BALLOC(b); + q->dlen += n; + QDEBUG checkb(b, "qbwrite"); + b = nil; + + /* make sure other end gets awakened */ + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + iunlock(&q->lk); + + /* get output going again */ + if(q->kick && (dowakeup || (q->state&Qkick))) + q->kick(q->arg); + + /* wakeup anyone consuming at the other end */ + if(dowakeup){ + wakeup(&q->rr); + + /* if we just wokeup a higher priority process, let it run */ + /* + p = wakeup(&q->rr); + if(p != nil && p->priority > up->priority) + sched(); + */ + } + + /* + * flow control, wait for queue to get below the limit + * before allowing the process to continue and queue + * more. We do this here so that postnote can only + * interrupt us after the data has been queued. This + * means that things like 9p flushes and ssl messages + * will not be disrupted by software interrupts. + * + * Note - this is moderately dangerous since a process + * that keeps getting interrupted and rewriting will + * queue infinite crud. + */ + for(;;){ + if(q->noblock || qnotfull(q)) + break; + + ilock(&q->lk); + q->state |= Qflow; + iunlock(&q->lk); + sleep(&q->wr, qnotfull, q); + } + USED(b); + + qunlock(&q->wlock); + poperror(); + return n; +} + +/* + * write to a queue. only Maxatomic bytes at a time is atomic. + */ +int +qwrite(Queue *q, void *vp, int len) +{ + int n, sofar; + Block *b; + uchar *p = vp; + + QDEBUG if(!islo()) + print("qwrite hi %p\n", getcallerpc(&q)); + + sofar = 0; + do { + n = len-sofar; + if(n > Maxatomic) + n = Maxatomic; + + b = allocb(n); + /* setmalloctag(b, (up->text[0]<<24)|(up->text[1]<<16)|(up->text[2]<<8)|up->text[3]); */ + if(waserror()){ + freeb(b); + nexterror(); + } + memmove(b->wp, p+sofar, n); + poperror(); + b->wp += n; + + qbwrite(q, b); + + sofar += n; + } while(sofar < len && (q->state & Qmsg) == 0); + + return len; +} + +/* + * used by print() to write to a queue. Since we may be splhi or not in + * a process, don't qlock. + */ +int +qiwrite(Queue *q, void *vp, int len) +{ + int n, sofar, dowakeup; + Block *b; + uchar *p = vp; + + dowakeup = 0; + + sofar = 0; + do { + n = len-sofar; + if(n > Maxatomic) + n = Maxatomic; + + b = iallocb(n); + if(b == nil) + break; + memmove(b->wp, p+sofar, n); + b->wp += n; + + ilock(&q->lk); + + QDEBUG checkb(b, "qiwrite"); + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + q->blast = b; + q->len += BALLOC(b); + q->dlen += n; + + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + + iunlock(&q->lk); + + if(dowakeup){ + if(q->kick) + q->kick(q->arg); + wakeup(&q->rr); + } + + sofar += n; + } while(sofar < len && (q->state & Qmsg) == 0); + + return sofar; +} + +/* + * be extremely careful when calling this, + * as there is no reference accounting + */ +void +qfree(Queue *q) +{ + qclose(q); + free(q); +} + +/* + * Mark a queue as closed. No further IO is permitted. + * All blocks are released. + */ +void +qclose(Queue *q) +{ + Block *bfirst; + + if(q == nil) + return; + + /* mark it */ + ilock(&q->lk); + q->state |= Qclosed; + q->state &= ~(Qflow|Qstarve); + strcpy(q->err, Ehungup); + bfirst = q->bfirst; + q->bfirst = 0; + q->len = 0; + q->dlen = 0; + q->noblock = 0; + iunlock(&q->lk); + + /* free queued blocks */ + freeblist(bfirst); + + /* wake up readers/writers */ + wakeup(&q->rr); + wakeup(&q->wr); +} + +/* + * Mark a queue as closed. Wakeup any readers. Don't remove queued + * blocks. + */ +void +qhangup(Queue *q, char *msg) +{ + /* mark it */ + ilock(&q->lk); + q->state |= Qclosed; + if(msg == 0 || *msg == 0) + strcpy(q->err, Ehungup); + else + strncpy(q->err, msg, ERRMAX-1); + iunlock(&q->lk); + + /* wake up readers/writers */ + wakeup(&q->rr); + wakeup(&q->wr); +} + +/* + * return non-zero if the q is hungup + */ +int +qisclosed(Queue *q) +{ + return q->state & Qclosed; +} + +/* + * mark a queue as no longer hung up + */ +void +qreopen(Queue *q) +{ + ilock(&q->lk); + q->state &= ~Qclosed; + q->state |= Qstarve; + q->eof = 0; + q->limit = q->inilim; + iunlock(&q->lk); +} + +/* + * return bytes queued + */ +int +qlen(Queue *q) +{ + return q->dlen; +} + +/* + * return space remaining before flow control + */ +int +qwindow(Queue *q) +{ + int l; + + l = q->limit - q->len; + if(l < 0) + l = 0; + return l; +} + +/* + * return true if we can read without blocking + */ +int +qcanread(Queue *q) +{ + return q->bfirst!=0; +} + +/* + * change queue limit + */ +void +qsetlimit(Queue *q, int limit) +{ + q->limit = limit; +} + +/* + * set blocking/nonblocking + */ +void +qnoblock(Queue *q, int onoff) +{ + q->noblock = onoff; +} + +/* + * flush the output queue + */ +void +qflush(Queue *q) +{ + Block *bfirst; + + /* mark it */ + ilock(&q->lk); + bfirst = q->bfirst; + q->bfirst = 0; + q->len = 0; + q->dlen = 0; + iunlock(&q->lk); + + /* free queued blocks */ + freeblist(bfirst); + + /* wake up readers/writers */ + wakeup(&q->wr); +} + +int +qfull(Queue *q) +{ + return q->state & Qflow; +} + +int +qstate(Queue *q) +{ + return q->state; +} diff --git a/sys/src/cmd/unix/drawterm/kern/qlock.c b/sys/src/cmd/unix/drawterm/kern/qlock.c new file mode 100755 index 000000000..fdbeaeac3 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/qlock.c @@ -0,0 +1,94 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" + +static void +queue(Proc **first, Proc **last) +{ + Proc *t; + + t = *last; + if(t == 0) + *first = up; + else + t->qnext = up; + *last = up; + up->qnext = 0; +} + +static Proc* +dequeue(Proc **first, Proc **last) +{ + Proc *t; + + t = *first; + if(t == 0) + return 0; + *first = t->qnext; + if(*first == 0) + *last = 0; + return t; +} + +void +qlock(QLock *q) +{ + lock(&q->lk); + + if(q->hold == 0) { + q->hold = up; + unlock(&q->lk); + return; + } + + /* + * Can't assert this because of RWLock + assert(q->hold != up); + */ + + queue((Proc**)&q->first, (Proc**)&q->last); + unlock(&q->lk); + procsleep(); +} + +int +canqlock(QLock *q) +{ + lock(&q->lk); + if(q->hold == 0) { + q->hold = up; + unlock(&q->lk); + return 1; + } + unlock(&q->lk); + return 0; +} + +void +qunlock(QLock *q) +{ + Proc *p; + + lock(&q->lk); + /* + * Can't assert this because of RWlock + assert(q->hold == CT); + */ + p = dequeue((Proc**)&q->first, (Proc**)&q->last); + if(p) { + q->hold = p; + unlock(&q->lk); + procwakeup(p); + } else { + q->hold = 0; + unlock(&q->lk); + } +} + +int +holdqlock(QLock *q) +{ + return q->hold == up; +} + diff --git a/sys/src/cmd/unix/drawterm/kern/rendez.c b/sys/src/cmd/unix/drawterm/kern/rendez.c new file mode 100755 index 000000000..6a9ad174e --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/rendez.c @@ -0,0 +1,90 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +void +sleep(Rendez *r, int (*f)(void*), void *arg) +{ + int s; + + s = splhi(); + + lock(&r->lk); + lock(&up->rlock); + if(r->p){ + print("double sleep %lud %lud\n", r->p->pid, up->pid); + dumpstack(); + } + + /* + * Wakeup only knows there may be something to do by testing + * r->p in order to get something to lock on. + * Flush that information out to memory in case the sleep is + * committed. + */ + r->p = up; + + if((*f)(arg) || up->notepending){ + /* + * if condition happened or a note is pending + * never mind + */ + r->p = nil; + unlock(&up->rlock); + unlock(&r->lk); + } else { + /* + * now we are committed to + * change state and call scheduler + */ + up->state = Wakeme; + up->r = r; + + /* statistics */ + /* m->cs++; */ + + unlock(&up->rlock); + unlock(&r->lk); + + procsleep(); + } + + if(up->notepending) { + up->notepending = 0; + splx(s); + error(Eintr); + } + + splx(s); +} + +Proc* +wakeup(Rendez *r) +{ + Proc *p; + int s; + + s = splhi(); + + lock(&r->lk); + p = r->p; + + if(p != nil){ + lock(&p->rlock); + if(p->state != Wakeme || p->r != r) + panic("wakeup: state"); + r->p = nil; + p->r = nil; + p->state = Running; + procwakeup(p); + unlock(&p->rlock); + } + unlock(&r->lk); + + splx(s); + + return p; +} + diff --git a/sys/src/cmd/unix/drawterm/kern/rwlock.c b/sys/src/cmd/unix/drawterm/kern/rwlock.c new file mode 100755 index 000000000..3381957a3 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/rwlock.c @@ -0,0 +1,39 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +void +rlock(RWlock *l) +{ + qlock(&l->x); /* wait here for writers and exclusion */ + lock(&l->lk); + l->readers++; + canqlock(&l->k); /* block writers if we are the first reader */ + unlock(&l->lk); + qunlock(&l->x); +} + +void +runlock(RWlock *l) +{ + lock(&l->lk); + if(--l->readers == 0) /* last reader out allows writers */ + qunlock(&l->k); + unlock(&l->lk); +} + +void +wlock(RWlock *l) +{ + qlock(&l->x); /* wait here for writers and exclusion */ + qlock(&l->k); /* wait here for last reader */ +} + +void +wunlock(RWlock *l) +{ + qunlock(&l->k); + qunlock(&l->x); +} diff --git a/sys/src/cmd/unix/drawterm/kern/screen.h b/sys/src/cmd/unix/drawterm/kern/screen.h new file mode 100755 index 000000000..dfdc12de5 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/screen.h @@ -0,0 +1,63 @@ +typedef struct Mouseinfo Mouseinfo; +typedef struct Mousestate Mousestate; +typedef struct Cursorinfo Cursorinfo; +typedef struct Screeninfo Screeninfo; + +#define Mousequeue 16 /* queue can only have Mousequeue-1 elements */ +#define Mousewindow 500 /* mouse event window in millisec */ + +struct Mousestate { + int buttons; + Point xy; + ulong msec; +}; + +struct Mouseinfo { + Lock lk; + Mousestate queue[Mousequeue]; + int ri, wi; + int lastb; + int trans; + int open; + Rendez r; +}; + +struct Cursorinfo { + Lock lk; + Point offset; + uchar clr[2*16]; + uchar set[2*16]; +}; + +struct Screeninfo { + Lock lk; + Memimage *newsoft; + int reshaped; + int depth; + int dibtype; +}; + +extern Memimage *gscreen; +extern Mouseinfo mouse; +extern Cursorinfo cursor; +extern Screeninfo screen; + +void screeninit(void); +void screenload(Rectangle, int, uchar *, Point, int); + +void getcolor(ulong, ulong*, ulong*, ulong*); +void setcolor(ulong, ulong, ulong, ulong); + +void refreshrect(Rectangle); + +void cursorarrow(void); +void setcursor(void); +void mouseset(Point); +void drawflushr(Rectangle); +void flushmemscreen(Rectangle); +uchar *attachscreen(Rectangle*, ulong*, int*, int*, int*, void**); + +void drawqlock(void); +void drawqunlock(void); +int drawcanqlock(void); +void terminit(void); diff --git a/sys/src/cmd/unix/drawterm/kern/sleep.c b/sys/src/cmd/unix/drawterm/kern/sleep.c new file mode 100755 index 000000000..6a9ad174e --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/sleep.c @@ -0,0 +1,90 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +void +sleep(Rendez *r, int (*f)(void*), void *arg) +{ + int s; + + s = splhi(); + + lock(&r->lk); + lock(&up->rlock); + if(r->p){ + print("double sleep %lud %lud\n", r->p->pid, up->pid); + dumpstack(); + } + + /* + * Wakeup only knows there may be something to do by testing + * r->p in order to get something to lock on. + * Flush that information out to memory in case the sleep is + * committed. + */ + r->p = up; + + if((*f)(arg) || up->notepending){ + /* + * if condition happened or a note is pending + * never mind + */ + r->p = nil; + unlock(&up->rlock); + unlock(&r->lk); + } else { + /* + * now we are committed to + * change state and call scheduler + */ + up->state = Wakeme; + up->r = r; + + /* statistics */ + /* m->cs++; */ + + unlock(&up->rlock); + unlock(&r->lk); + + procsleep(); + } + + if(up->notepending) { + up->notepending = 0; + splx(s); + error(Eintr); + } + + splx(s); +} + +Proc* +wakeup(Rendez *r) +{ + Proc *p; + int s; + + s = splhi(); + + lock(&r->lk); + p = r->p; + + if(p != nil){ + lock(&p->rlock); + if(p->state != Wakeme || p->r != r) + panic("wakeup: state"); + r->p = nil; + p->r = nil; + p->state = Running; + procwakeup(p); + unlock(&p->rlock); + } + unlock(&r->lk); + + splx(s); + + return p; +} + diff --git a/sys/src/cmd/unix/drawterm/kern/smalloc.c b/sys/src/cmd/unix/drawterm/kern/smalloc.c new file mode 100755 index 000000000..314f3c850 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/smalloc.c @@ -0,0 +1,18 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +void* +smalloc(ulong n) +{ + return mallocz(n, 1); +} + +void* +malloc(ulong n) +{ + return mallocz(n, 1); +} + diff --git a/sys/src/cmd/unix/drawterm/kern/stub.c b/sys/src/cmd/unix/drawterm/kern/stub.c new file mode 100755 index 000000000..d9c690638 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/stub.c @@ -0,0 +1,171 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +void +mallocsummary(void) +{ +} + +void +pagersummary(void) +{ +} + +int +iseve(void) +{ + return 1; +} + +void +setswapchan(Chan *c) +{ + USED(c); +} + +void +splx(int x) +{ + USED(x); +} + +int +splhi(void) +{ + return 0; +} + +int +spllo(void) +{ + return 0; +} + +void +procdump(void) +{ +} + +void +scheddump(void) +{ +} + +void +killbig(void) +{ +} + +void +dumpstack(void) +{ +} + +void +xsummary(void) +{ +} + +void +rebootcmd(int argc, char **argv) +{ + USED(argc); + USED(argv); +} + +void +kickpager(void) +{ +} + +int +userwrite(char *a, int n) +{ + error(Eperm); + return 0; +} + +vlong +todget(vlong *p) +{ + if(p) + *p = 0; + return 0; +} + +void +todset(vlong a, vlong b, int c) +{ + USED(a); + USED(b); + USED(c); +} + +void +todsetfreq(vlong a) +{ + USED(a); +} + +long +hostdomainwrite(char *a, int n) +{ + USED(a); + USED(n); + error(Eperm); + return 0; +} + +long +hostownerwrite(char *a, int n) +{ + USED(a); + USED(n); + error(Eperm); + return 0; +} + +void +todinit(void) +{ +} + +void +rdb(void) +{ +} + +void +setmalloctag(void *v, uintptr tag) +{ + USED(v); + USED(tag); +} + +int +postnote(Proc *p, int x, char *msg, int flag) +{ + USED(p); + USED(x); + USED(msg); + USED(flag); + return 0; +} + +void +exhausted(char *s) +{ + panic("out of %s", s); +} + +uvlong +fastticks(uvlong *v) +{ + if(v) + *v = 1; + return 0; +} + diff --git a/sys/src/cmd/unix/drawterm/kern/sysfile.c b/sys/src/cmd/unix/drawterm/kern/sysfile.c new file mode 100755 index 000000000..18dadbcda --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/sysfile.c @@ -0,0 +1,1244 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include "user.h" +#undef open +#undef mount +#undef read +#undef write +#undef seek +#undef stat +#undef wstat +#undef remove +#undef close +#undef fstat +#undef fwstat + +/* + * The sys*() routines needn't poperror() as they return directly to syscall(). + */ + +static void +unlockfgrp(Fgrp *f) +{ + int ex; + + ex = f->exceed; + f->exceed = 0; + unlock(&f->ref.lk); + if(ex) + pprint("warning: process exceeds %d file descriptors\n", ex); +} + +int +growfd(Fgrp *f, int fd) /* fd is always >= 0 */ +{ + Chan **newfd, **oldfd; + + if(fd < f->nfd) + return 0; + if(fd >= f->nfd+DELTAFD) + return -1; /* out of range */ + /* + * Unbounded allocation is unwise; besides, there are only 16 bits + * of fid in 9P + */ + if(f->nfd >= 5000){ + Exhausted: + print("no free file descriptors\n"); + return -1; + } + newfd = malloc((f->nfd+DELTAFD)*sizeof(Chan*)); + if(newfd == 0) + goto Exhausted; + oldfd = f->fd; + memmove(newfd, oldfd, f->nfd*sizeof(Chan*)); + f->fd = newfd; + free(oldfd); + f->nfd += DELTAFD; + if(fd > f->maxfd){ + if(fd/100 > f->maxfd/100) + f->exceed = (fd/100)*100; + f->maxfd = fd; + } + return 1; +} + +/* + * this assumes that the fgrp is locked + */ +int +findfreefd(Fgrp *f, int start) +{ + int fd; + + for(fd=start; fd<f->nfd; fd++) + if(f->fd[fd] == 0) + break; + if(fd >= f->nfd && growfd(f, fd) < 0) + return -1; + return fd; +} + +int +newfd(Chan *c) +{ + int fd; + Fgrp *f; + + f = up->fgrp; + lock(&f->ref.lk); + fd = findfreefd(f, 0); + if(fd < 0){ + unlockfgrp(f); + return -1; + } + if(fd > f->maxfd) + f->maxfd = fd; + f->fd[fd] = c; + unlockfgrp(f); + return fd; +} + +int +newfd2(int fd[2], Chan *c[2]) +{ + Fgrp *f; + + f = up->fgrp; + lock(&f->ref.lk); + fd[0] = findfreefd(f, 0); + if(fd[0] < 0){ + unlockfgrp(f); + return -1; + } + fd[1] = findfreefd(f, fd[0]+1); + if(fd[1] < 0){ + unlockfgrp(f); + return -1; + } + if(fd[1] > f->maxfd) + f->maxfd = fd[1]; + f->fd[fd[0]] = c[0]; + f->fd[fd[1]] = c[1]; + unlockfgrp(f); + + return 0; +} + +Chan* +fdtochan(int fd, int mode, int chkmnt, int iref) +{ + Chan *c; + Fgrp *f; + + c = 0; + f = up->fgrp; + + lock(&f->ref.lk); + if(fd<0 || f->nfd<=fd || (c = f->fd[fd])==0) { + unlock(&f->ref.lk); + error(Ebadfd); + } + if(iref) + incref(&c->ref); + unlock(&f->ref.lk); + + if(chkmnt && (c->flag&CMSG)) { + if(iref) + cclose(c); + error(Ebadusefd); + } + + if(mode<0 || c->mode==ORDWR) + return c; + + if((mode&OTRUNC) && c->mode==OREAD) { + if(iref) + cclose(c); + error(Ebadusefd); + } + + if((mode&~OTRUNC) != c->mode) { + if(iref) + cclose(c); + error(Ebadusefd); + } + + return c; +} + +int +openmode(ulong o) +{ + o &= ~(OTRUNC|OCEXEC|ORCLOSE); + if(o > OEXEC) + error(Ebadarg); + if(o == OEXEC) + return OREAD; + return o; +} + +long +_sysfd2path(int fd, char *buf, uint nbuf) +{ + Chan *c; + + c = fdtochan(fd, -1, 0, 1); + + if(c->name == nil) + snprint(buf, nbuf, "<null>"); + else + snprint(buf, nbuf, "%s", c->name->s); + cclose(c); + return 0; +} + +long +_syspipe(int fd[2]) +{ + Chan *c[2]; + Dev *d; + static char *datastr[] = {"data", "data1"}; + + d = devtab[devno('|', 0)]; + c[0] = namec("#|", Atodir, 0, 0); + c[1] = 0; + fd[0] = -1; + fd[1] = -1; + + if(waserror()){ + cclose(c[0]); + if(c[1]) + cclose(c[1]); + nexterror(); + } + c[1] = cclone(c[0]); + if(walk(&c[0], datastr+0, 1, 1, nil) < 0) + error(Egreg); + if(walk(&c[1], datastr+1, 1, 1, nil) < 0) + error(Egreg); + c[0] = d->open(c[0], ORDWR); + c[1] = d->open(c[1], ORDWR); + if(newfd2(fd, c) < 0) + error(Enofd); + poperror(); + + return 0; +} + +long +_sysdup(int fd0, int fd1) +{ + int fd; + Chan *c, *oc; + Fgrp *f = up->fgrp; + + /* + * Close after dup'ing, so date > #d/1 works + */ + c = fdtochan(fd0, -1, 0, 1); + fd = fd1; + if(fd != -1){ + lock(&f->ref.lk); + if(fd<0 || growfd(f, fd)<0) { + unlockfgrp(f); + cclose(c); + error(Ebadfd); + } + if(fd > f->maxfd) + f->maxfd = fd; + + oc = f->fd[fd]; + f->fd[fd] = c; + unlockfgrp(f); + if(oc) + cclose(oc); + }else{ + if(waserror()) { + cclose(c); + nexterror(); + } + fd = newfd(c); + if(fd < 0) + error(Enofd); + poperror(); + } + + return fd; +} + +long +_sysopen(char *name, int mode) +{ + int fd; + Chan *c = 0; + + openmode(mode); /* error check only */ + if(waserror()){ + if(c) + cclose(c); + nexterror(); + } + c = namec(name, Aopen, mode, 0); + fd = newfd(c); + if(fd < 0) + error(Enofd); + poperror(); + return fd; +} + +void +fdclose(int fd, int flag) +{ + int i; + Chan *c; + Fgrp *f = up->fgrp; + + lock(&f->ref.lk); + c = f->fd[fd]; + if(c == 0){ + /* can happen for users with shared fd tables */ + unlock(&f->ref.lk); + return; + } + if(flag){ + if(c==0 || !(c->flag&flag)){ + unlock(&f->ref.lk); + return; + } + } + f->fd[fd] = 0; + if(fd == f->maxfd) + for(i=fd; --i>=0 && f->fd[i]==0; ) + f->maxfd = i; + + unlock(&f->ref.lk); + cclose(c); +} + +long +_sysclose(int fd) +{ + fdtochan(fd, -1, 0, 0); + fdclose(fd, 0); + + return 0; +} + +long +unionread(Chan *c, void *va, long n) +{ + int i; + long nr; + Mhead *m; + Mount *mount; + + qlock(&c->umqlock); + m = c->umh; + rlock(&m->lock); + mount = m->mount; + /* bring mount in sync with c->uri and c->umc */ + for(i = 0; mount != nil && i < c->uri; i++) + mount = mount->next; + + nr = 0; + while(mount != nil) { + /* Error causes component of union to be skipped */ + if(mount->to && !waserror()) { + if(c->umc == nil){ + c->umc = cclone(mount->to); + c->umc = devtab[c->umc->type]->open(c->umc, OREAD); + } + + nr = devtab[c->umc->type]->read(c->umc, va, n, c->umc->offset); + c->umc->offset += nr; + poperror(); + } + if(nr > 0) + break; + + /* Advance to next element */ + c->uri++; + if(c->umc) { + cclose(c->umc); + c->umc = nil; + } + mount = mount->next; + } + runlock(&m->lock); + qunlock(&c->umqlock); + return nr; +} + +static long +kread(int fd, void *buf, long n, vlong *offp) +{ + int dir; + Chan *c; + vlong off; + + c = fdtochan(fd, OREAD, 1, 1); + + if(waserror()) { + cclose(c); + nexterror(); + } + + dir = c->qid.type&QTDIR; + /* + * The offset is passed through on directories, normally. sysseek complains but + * pread is used by servers and e.g. exportfs that shouldn't need to worry about this issue. + */ + + if(offp == nil) /* use and maintain channel's offset */ + off = c->offset; + else + off = *offp; + + if(off < 0) + error(Enegoff); + + if(dir && c->umh) + n = unionread(c, buf, n); + else + n = devtab[c->type]->read(c, buf, n, off); + + if(offp == nil){ + lock(&c->ref.lk); + c->offset += n; + unlock(&c->ref.lk); + } + + poperror(); + cclose(c); + + return n; +} + +/* name conflicts with netbsd +long +_sys_read(int fd, void *buf, long n) +{ + return kread(fd, buf, n, nil); +} +*/ + +long +_syspread(int fd, void *buf, long n, vlong off) +{ + if(off == ((uvlong) ~0)) + return kread(fd, buf, n, nil); + return kread(fd, buf, n, &off); +} + +static long +kwrite(int fd, void *buf, long nn, vlong *offp) +{ + Chan *c; + long m, n; + vlong off; + + n = 0; + c = fdtochan(fd, OWRITE, 1, 1); + if(waserror()) { + if(offp == nil){ + lock(&c->ref.lk); + c->offset -= n; + unlock(&c->ref.lk); + } + cclose(c); + nexterror(); + } + + if(c->qid.type & QTDIR) + error(Eisdir); + + n = nn; + + if(offp == nil){ /* use and maintain channel's offset */ + lock(&c->ref.lk); + off = c->offset; + c->offset += n; + unlock(&c->ref.lk); + }else + off = *offp; + + if(off < 0) + error(Enegoff); + + m = devtab[c->type]->write(c, buf, n, off); + + if(offp == nil && m < n){ + lock(&c->ref.lk); + c->offset -= n - m; + unlock(&c->ref.lk); + } + + poperror(); + cclose(c); + + return m; +} + +long +sys_write(int fd, void *buf, long n) +{ + return kwrite(fd, buf, n, nil); +} + +long +_syspwrite(int fd, void *buf, long n, vlong off) +{ + if(off == ((uvlong) ~0)) + return kwrite(fd, buf, n, nil); + return kwrite(fd, buf, n, &off); +} + +static vlong +_sysseek(int fd, vlong off, int whence) +{ + Chan *c; + uchar buf[sizeof(Dir)+100]; + Dir dir; + int n; + + c = fdtochan(fd, -1, 1, 1); + if(waserror()){ + cclose(c); + nexterror(); + } + if(devtab[c->type]->dc == '|') + error(Eisstream); + + switch(whence){ + case 0: + if((c->qid.type & QTDIR) && off != 0) + error(Eisdir); + if(off < 0) + error(Enegoff); + c->offset = off; + break; + + case 1: + if(c->qid.type & QTDIR) + error(Eisdir); + lock(&c->ref.lk); /* lock for read/write update */ + off = off + c->offset; + if(off < 0) + error(Enegoff); + c->offset = off; + unlock(&c->ref.lk); + break; + + case 2: + if(c->qid.type & QTDIR) + error(Eisdir); + n = devtab[c->type]->stat(c, buf, sizeof buf); + if(convM2D(buf, n, &dir, nil) == 0) + error("internal error: stat error in seek"); + off = dir.length + off; + if(off < 0) + error(Enegoff); + c->offset = off; + break; + + default: + error(Ebadarg); + } + c->uri = 0; + c->dri = 0; + cclose(c); + poperror(); + return off; +} + +void +validstat(uchar *s, int n) +{ + int m; + char buf[64]; + + if(statcheck(s, n) < 0) + error(Ebadstat); + /* verify that name entry is acceptable */ + s += STATFIXLEN - 4*BIT16SZ; /* location of first string */ + /* + * s now points at count for first string. + * if it's too long, let the server decide; this is + * only for his protection anyway. otherwise + * we'd have to allocate and waserror. + */ + m = GBIT16(s); + s += BIT16SZ; + if(m+1 > sizeof buf) + return; + memmove(buf, s, m); + buf[m] = '\0'; + /* name could be '/' */ + if(strcmp(buf, "/") != 0) + validname(buf, 0); +} + +long +_sysfstat(int fd, void *buf, long n) +{ + Chan *c; + uint l; + + l = n; + validaddr(buf, l, 1); + c = fdtochan(fd, -1, 0, 1); + if(waserror()) { + cclose(c); + nexterror(); + } + l = devtab[c->type]->stat(c, buf, l); + poperror(); + cclose(c); + return l; +} + +long +_sysstat(char *name, void *buf, long n) +{ + Chan *c; + uint l; + + l = n; + validaddr(buf, l, 1); + validaddr(name, 1, 0); + c = namec(name, Aaccess, 0, 0); + if(waserror()){ + cclose(c); + nexterror(); + } + l = devtab[c->type]->stat(c, buf, l); + poperror(); + cclose(c); + return l; +} + +long +_syschdir(char *name) +{ + Chan *c; + + validaddr(name, 1, 0); + + c = namec(name, Atodir, 0, 0); + cclose(up->dot); + up->dot = c; + return 0; +} + +long +bindmount(int ismount, int fd, int afd, char* arg0, char* arg1, ulong flag, char* spec) +{ + int ret; + Chan *c0, *c1, *ac, *bc; + struct{ + Chan *chan; + Chan *authchan; + char *spec; + int flags; + }bogus; + + if((flag&~MMASK) || (flag&MORDER)==(MBEFORE|MAFTER)) + error(Ebadarg); + + bogus.flags = flag & MCACHE; + + if(ismount){ + if(up->pgrp->noattach) + error(Enoattach); + + ac = nil; + bc = fdtochan(fd, ORDWR, 0, 1); + if(waserror()) { + if(ac) + cclose(ac); + cclose(bc); + nexterror(); + } + + if(afd >= 0) + ac = fdtochan(afd, ORDWR, 0, 1); + + bogus.chan = bc; + bogus.authchan = ac; + + validaddr((ulong)spec, 1, 0); + bogus.spec = spec; + if(waserror()) + error(Ebadspec); + validname(spec, 1); + poperror(); + + ret = devno('M', 0); + c0 = devtab[ret]->attach((char*)&bogus); + + poperror(); + if(ac) + cclose(ac); + cclose(bc); + }else{ + bogus.spec = 0; + validaddr((ulong)arg0, 1, 0); + c0 = namec(arg0, Abind, 0, 0); + } + + if(waserror()){ + cclose(c0); + nexterror(); + } + + validaddr((ulong)arg1, 1, 0); + c1 = namec(arg1, Amount, 0, 0); + if(waserror()){ + cclose(c1); + nexterror(); + } + + ret = cmount(&c0, c1, flag, bogus.spec); + + poperror(); + cclose(c1); + poperror(); + cclose(c0); + if(ismount) + fdclose(fd, 0); + + return ret; +} + +long +_sysbind(char *old, char *new, int flag) +{ + return bindmount(0, -1, -1, old, new, flag, nil); +} + +long +_sysmount(int fd, int afd, char *new, int flag, char *spec) +{ + return bindmount(1, fd, afd, nil, new, flag, spec); +} + +long +_sysunmount(char *old, char *new) +{ + Chan *cmount, *cmounted; + + cmounted = 0; + + cmount = namec(new, Amount, 0, 0); + + if(old) { + if(waserror()) { + cclose(cmount); + nexterror(); + } + validaddr(old, 1, 0); + /* + * This has to be namec(..., Aopen, ...) because + * if arg[0] is something like /srv/cs or /fd/0, + * opening it is the only way to get at the real + * Chan underneath. + */ + cmounted = namec(old, Aopen, OREAD, 0); + poperror(); + } + + if(waserror()) { + cclose(cmount); + if(cmounted) + cclose(cmounted); + nexterror(); + } + + cunmount(cmount, cmounted); + cclose(cmount); + if(cmounted) + cclose(cmounted); + poperror(); + return 0; +} + +long +_syscreate(char *name, int mode, ulong perm) +{ + int fd; + Chan *c = 0; + + openmode(mode&~OEXCL); /* error check only; OEXCL okay here */ + if(waserror()) { + if(c) + cclose(c); + nexterror(); + } + validaddr(name, 1, 0); + c = namec(name, Acreate, mode, perm); + fd = newfd(c); + if(fd < 0) + error(Enofd); + poperror(); + return fd; +} + +long +_sysremove(char *name) +{ + Chan *c; + + c = namec(name, Aremove, 0, 0); + if(waserror()){ + c->type = 0; /* see below */ + cclose(c); + nexterror(); + } + devtab[c->type]->remove(c); + /* + * Remove clunks the fid, but we need to recover the Chan + * so fake it up. rootclose() is known to be a nop. + */ + c->type = 0; + poperror(); + cclose(c); + return 0; +} + +long +_syswstat(char *name, void *buf, long n) +{ + Chan *c; + uint l; + + l = n; + validstat(buf, l); + validaddr(name, 1, 0); + c = namec(name, Aaccess, 0, 0); + if(waserror()){ + cclose(c); + nexterror(); + } + l = devtab[c->type]->wstat(c, buf, l); + poperror(); + cclose(c); + return l; +} + +long +_sysfwstat(int fd, void *buf, long n) +{ + Chan *c; + uint l; + + l = n; + validaddr(buf, l, 0); + validstat(buf, l); + c = fdtochan(fd, -1, 1, 1); + if(waserror()) { + cclose(c); + nexterror(); + } + l = devtab[c->type]->wstat(c, buf, l); + poperror(); + cclose(c); + return l; +} + + +static void +starterror(void) +{ + assert(up->nerrlab == 0); +} + +static void +_syserror(void) +{ + char *p; + + p = up->syserrstr; + up->syserrstr = up->errstr; + up->errstr = p; +} + +static void +enderror(void) +{ + assert(up->nerrlab == 1); + poperror(); +} + +int +sysbind(char *old, char *new, int flag) +{ + int n; + + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _sysbind(old, new, flag); + enderror(); + return n; +} + +int +syschdir(char *path) +{ + int n; + + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _syschdir(path); + enderror(); + return n; +} + +int +sysclose(int fd) +{ + int n; + + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _sysclose(fd); + enderror(); + return n; +} + +int +syscreate(char *name, int mode, ulong perm) +{ + int n; + + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _syscreate(name, mode, perm); + enderror(); + return n; +} + +int +sysdup(int fd0, int fd1) +{ + int n; + + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _sysdup(fd0, fd1); + enderror(); + return n; +} + +int +sysfstat(int fd, uchar *buf, int n) +{ + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _sysfstat(fd, buf, n); + enderror(); + return n; +} + +int +sysfwstat(int fd, uchar *buf, int n) +{ + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _sysfwstat(fd, buf, n); + enderror(); + return n; +} + +int +sysmount(int fd, int afd, char *new, int flag, char *spec) +{ + int n; + + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _sysmount(fd, afd, new, flag, spec); + enderror(); + return n; +} + +int +sysunmount(char *old, char *new) +{ + int n; + + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _sysunmount(old, new); + enderror(); + return n; +} + +int +sysopen(char *name, int mode) +{ + int n; + + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _sysopen(name, mode); + enderror(); + return n; +} + +int +syspipe(int *fd) +{ + int n; + + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _syspipe(fd); + enderror(); + return n; +} + +long +syspread(int fd, void *buf, long n, vlong off) +{ + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _syspread(fd, buf, n, off); + enderror(); + return n; +} + +long +syspwrite(int fd, void *buf, long n, vlong off) +{ + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _syspwrite(fd, buf, n, off); + enderror(); + return n; +} + +long +sysread(int fd, void *buf, long n) +{ + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _syspread(fd, buf, n, (uvlong) ~0); + enderror(); + return n; +} + +int +sysremove(char *path) +{ + int n; + + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _sysremove(path); + enderror(); + return n; +} + +vlong +sysseek(int fd, vlong off, int whence) +{ + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + off = _sysseek(fd, off, whence); + enderror(); + return off; +} + +int +sysstat(char *name, uchar *buf, int n) +{ + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _sysstat(name, buf, n); + enderror(); + return n; +} + +long +syswrite(int fd, void *buf, long n) +{ + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _syspwrite(fd, buf, n, (uvlong) ~0); + enderror(); + return n; +} + +int +syswstat(char *name, uchar *buf, int n) +{ + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _syswstat(name, buf, n); + enderror(); + return n; +} + +void +werrstr(char *f, ...) +{ + char buf[ERRMAX]; + va_list arg; + + va_start(arg, f); + vsnprint(buf, sizeof buf, f, arg); + va_end(arg); + + if(up->nerrlab) + strecpy(up->errstr, up->errstr+ERRMAX, buf); + else + strecpy(up->syserrstr, up->syserrstr+ERRMAX, buf); +} + +int +__errfmt(Fmt *fmt) +{ + if(up->nerrlab) + return fmtstrcpy(fmt, up->errstr); + else + return fmtstrcpy(fmt, up->syserrstr); +} + +int +errstr(char *buf, uint n) +{ + char tmp[ERRMAX]; + char *p; + + p = up->nerrlab ? up->errstr : up->syserrstr; + memmove(tmp, p, ERRMAX); + utfecpy(p, p+ERRMAX, buf); + utfecpy(buf, buf+n, tmp); + return strlen(buf); +} + +int +rerrstr(char *buf, uint n) +{ + char *p; + + p = up->nerrlab ? up->errstr : up->syserrstr; + utfecpy(buf, buf+n, p); + return strlen(buf); +} + +void* +_sysrendezvous(void* arg0, void* arg1) +{ + void *tag, *val; + Proc *p, **l; + + tag = arg0; + l = &REND(up->rgrp, (uintptr)tag); + up->rendval = (void*)~0; + + lock(&up->rgrp->ref.lk); + for(p = *l; p; p = p->rendhash) { + if(p->rendtag == tag) { + *l = p->rendhash; + val = p->rendval; + p->rendval = arg1; + + while(p->mach != 0) + ; + procwakeup(p); + unlock(&up->rgrp->ref.lk); + return val; + } + l = &p->rendhash; + } + + /* Going to sleep here */ + up->rendtag = tag; + up->rendval = arg1; + up->rendhash = *l; + *l = up; + up->state = Rendezvous; + unlock(&up->rgrp->ref.lk); + + procsleep(); + + return up->rendval; +} + +void* +sysrendezvous(void *tag, void *val) +{ + void *n; + + starterror(); + if(waserror()){ + _syserror(); + return (void*)~0; + } + n = _sysrendezvous(tag, val); + enderror(); + return n; +} diff --git a/sys/src/cmd/unix/drawterm/kern/sysproc.c b/sys/src/cmd/unix/drawterm/kern/sysproc.c new file mode 100755 index 000000000..11caeb773 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/sysproc.c @@ -0,0 +1,32 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +long +sysexits(ulong *arg) +{ + char *status; + char *inval = "invalid exit string"; + char buf[ERRMAX]; + + status = (char*)arg[0]; + if(status){ + if(waserror()) + status = inval; + else{ + validaddr((ulong)status, 1, 0); + if(vmemchr(status, 0, ERRMAX) == 0){ + memmove(buf, status, ERRMAX); + buf[ERRMAX-1] = 0; + status = buf; + } + } + poperror(); + + } + pexit(status, 1); + return 0; /* not reached */ +} + diff --git a/sys/src/cmd/unix/drawterm/kern/term.c b/sys/src/cmd/unix/drawterm/kern/term.c new file mode 100755 index 000000000..f8fa9cbbf --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/term.c @@ -0,0 +1,205 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include <draw.h> +#include <memdraw.h> +#include "screen.h" + +#define MINX 8 +#define Backgnd 0xFF /* white */ + + Memsubfont *memdefont; + +struct{ + Point pos; + int bwid; +}out; + +Lock screenlock; + +Memimage *conscol; +Memimage *back; +extern Memimage *gscreen; + +static Rectangle flushr; +static Rectangle window; +static Point curpos; +static int h; +static void termscreenputs(char*, int); + + +static void +screenflush(void) +{ + drawflushr(flushr); + flushr = Rect(10000, 10000, -10000, -10000); +} + +static void +addflush(Rectangle r) +{ + if(flushr.min.x >= flushr.max.x) + flushr = r; + else + combinerect(&flushr, r); +} + +static void +screenwin(void) +{ + Point p; + char *greet; + Memimage *grey; + + drawqlock(); + back = memwhite; + conscol = memblack; + memfillcolor(gscreen, 0x444488FF); + + h = memdefont->height; + + window.min = addpt(gscreen->r.min, Pt(20,20)); + window.max.x = window.min.x + Dx(gscreen->r)*3/4-40; + window.max.y = window.min.y + Dy(gscreen->r)*3/4-100; + + memimagedraw(gscreen, window, memblack, ZP, memopaque, ZP, S); + window = insetrect(window, 4); + memimagedraw(gscreen, window, memwhite, ZP, memopaque, ZP, S); + + /* a lot of work to get a grey color */ + grey = allocmemimage(Rect(0,0,1,1), CMAP8); + grey->flags |= Frepl; + grey->clipr = gscreen->r; + memfillcolor(grey, 0xAAAAAAFF); + memimagedraw(gscreen, Rect(window.min.x, window.min.y, + window.max.x, window.min.y+h+5+6), grey, ZP, nil, ZP, S); + freememimage(grey); + window = insetrect(window, 5); + + greet = " Plan 9 Console "; + p = addpt(window.min, Pt(10, 0)); + memimagestring(gscreen, p, conscol, ZP, memdefont, greet); + window.min.y += h+6; + curpos = window.min; + window.max.y = window.min.y+((window.max.y-window.min.y)/h)*h; + flushmemscreen(gscreen->r); + drawqunlock(); +} + +void +terminit(void) +{ + memdefont = getmemdefont(); + out.pos.x = MINX; + out.pos.y = 0; + out.bwid = memdefont->info[' '].width; + screenwin(); + screenputs = termscreenputs; +} + +static void +scroll(void) +{ + int o; + Point p; + Rectangle r; + + o = 8*h; + r = Rpt(window.min, Pt(window.max.x, window.max.y-o)); + p = Pt(window.min.x, window.min.y+o); + memimagedraw(gscreen, r, gscreen, p, nil, p, S); + r = Rpt(Pt(window.min.x, window.max.y-o), window.max); + memimagedraw(gscreen, r, back, ZP, nil, ZP, S); + flushmemscreen(gscreen->r); + curpos.y -= o; +} + +static void +screenputc(char *buf) +{ + Point p; + int w, pos; + Rectangle r; + static int *xp; + static int xbuf[256]; + + if(xp < xbuf || xp >= &xbuf[sizeof(xbuf)]) + xp = xbuf; + + switch(buf[0]) { + case '\n': + if(curpos.y+h >= window.max.y) + scroll(); + curpos.y += h; + screenputc("\r"); + break; + case '\r': + xp = xbuf; + curpos.x = window.min.x; + break; + case '\t': + p = memsubfontwidth(memdefont, " "); + w = p.x; + *xp++ = curpos.x; + pos = (curpos.x-window.min.x)/w; + pos = 8-(pos%8); + r = Rect(curpos.x, curpos.y, curpos.x+pos*w, curpos.y + h); + memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S); + addflush(r); + curpos.x += pos*w; + break; + case '\b': + if(xp <= xbuf) + break; + xp--; + r = Rect(*xp, curpos.y, curpos.x, curpos.y + h); + memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S); + addflush(r); + curpos.x = *xp; + break; + default: + p = memsubfontwidth(memdefont, buf); + w = p.x; + + if(curpos.x >= window.max.x-w) + screenputc("\n"); + + *xp++ = curpos.x; + r = Rect(curpos.x, curpos.y, curpos.x+w, curpos.y + h); + memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S); + memimagestring(gscreen, curpos, conscol, ZP, memdefont, buf); + addflush(r); + curpos.x += w; + } +} + +static void +termscreenputs(char *s, int n) +{ + int i, locked; + Rune r; + char buf[4]; + + lock(&screenlock); + locked = drawcanqlock(); + while(n > 0){ + i = chartorune(&r, s); + if(i == 0){ + s++; + --n; + continue; + } + memmove(buf, s, i); + buf[i] = 0; + n -= i; + s += i; + screenputc(buf); + } + if(locked) + drawqunlock(); + screenflush(); + unlock(&screenlock); +} diff --git a/sys/src/cmd/unix/drawterm/kern/uart.c b/sys/src/cmd/unix/drawterm/kern/uart.c new file mode 100755 index 000000000..da6d93e3d --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/uart.c @@ -0,0 +1,15 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +extern int panicking; +void +uartputs(char *s, int n) +{ + if(panicking) + write(1, s, n); +} + + diff --git a/sys/src/cmd/unix/drawterm/kern/unused/syscall.c b/sys/src/cmd/unix/drawterm/kern/unused/syscall.c new file mode 100755 index 000000000..1ae9a7cb2 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/unused/syscall.c @@ -0,0 +1,837 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +Chan* +fdtochan(int fd, int mode, int chkmnt, int iref) +{ + Fgrp *f; + Chan *c; + + c = 0; + f = up->fgrp; + + lock(&f->ref.lk); + if(fd<0 || NFD<=fd || (c = f->fd[fd])==0) { + unlock(&f->ref.lk); + error(Ebadfd); + } + if(iref) + refinc(&c->ref); + unlock(&f->ref.lk); + + if(chkmnt && (c->flag&CMSG)) + goto bad; + if(mode<0 || c->mode==ORDWR) + return c; + if((mode&OTRUNC) && c->mode==OREAD) + goto bad; + if((mode&~OTRUNC) != c->mode) + goto bad; + return c; +bad: + if(iref) + cclose(c); + error(Ebadusefd); + return nil; /* shut up compiler */ +} + +static void +fdclose(int fd, int flag) +{ + int i; + Chan *c; + Fgrp *f; + + f = up->fgrp; + + lock(&f->ref.lk); + c = f->fd[fd]; + if(c == 0) { + unlock(&f->ref.lk); + return; + } + if(flag) { + if(c==0 || !(c->flag&flag)) { + unlock(&f->ref.lk); + return; + } + } + f->fd[fd] = 0; + if(fd == f->maxfd) + for(i=fd; --i>=0 && f->fd[i]==0; ) + f->maxfd = i; + + unlock(&f->ref.lk); + cclose(c); +} + +static int +newfd(Chan *c) +{ + int i; + Fgrp *f; + + f = up->fgrp; + lock(&f->ref.lk); + for(i=0; i<NFD; i++) + if(f->fd[i] == 0){ + if(i > f->maxfd) + f->maxfd = i; + f->fd[i] = c; + unlock(&f->ref.lk); + return i; + } + unlock(&f->ref.lk); + error("no file descriptors"); + return 0; +} + +int +sysclose(int fd) +{ + if(waserror()) + return -1; + + fdtochan(fd, -1, 0, 0); + fdclose(fd, 0); + poperror(); + return 0; +} + +int +syscreate(char *path, int mode, ulong perm) +{ + int fd; + Chan *c = 0; + + if(waserror()) { + cclose(c); + return -1; + } + + openmode(mode); /* error check only */ + c = namec(path, Acreate, mode, perm); + fd = newfd((Chan*)c); + poperror(); + return fd; +} + +int +sysdup(int old, int new) +{ + Chan *oc; + Fgrp *f = up->fgrp; + Chan *c = 0; + + if(waserror()) + return -1; + + c = fdtochan(old, -1, 0, 1); + if(new != -1) { + if(new < 0 || NFD <= new) { + cclose(c); + error(Ebadfd); + } + lock(&f->ref.lk); + if(new > f->maxfd) + f->maxfd = new; + oc = f->fd[new]; + f->fd[new] = (Chan*)c; + unlock(&f->ref.lk); + if(oc != 0) + cclose(oc); + } + else { + if(waserror()) { + cclose(c); + nexterror(); + } + new = newfd((Chan*)c); + poperror(); + } + poperror(); + return new; +} + +int +sysfstat(int fd, char *buf) +{ + Chan *c = 0; + + if(waserror()) { + cclose(c); + return -1; + } + c = fdtochan(fd, -1, 0, 1); + devtab[c->type]->stat((Chan*)c, buf); + poperror(); + cclose(c); + return 0; +} + +int +sysfwstat(int fd, char *buf) +{ + Chan *c = 0; + + if(waserror()) { + cclose(c); + return -1; + } + nameok(buf); + c = fdtochan(fd, -1, 1, 1); + devtab[c->type]->wstat((Chan*)c, buf); + poperror(); + cclose(c); + return 0; +} + +int +syschdir(char *dir) +{ + return 0; +} + +long +bindmount(Chan *c0, char *old, int flag, char *spec) +{ + int ret; + Chan *c1 = 0; + + if(flag>MMASK || (flag&MORDER) == (MBEFORE|MAFTER)) + error(Ebadarg); + + c1 = namec(old, Amount, 0, 0); + if(waserror()){ + cclose(c1); + nexterror(); + } + + ret = cmount(c0, c1, flag, spec); + + poperror(); + cclose(c1); + return ret; +} + +int +sysbind(char *new, char *old, int flags) +{ + long r; + Chan *c0 = 0; + + if(waserror()) { + cclose(c0); + return -1; + } + c0 = namec(new, Aaccess, 0, 0); + r = bindmount(c0, old, flags, ""); + poperror(); + cclose(c0); + return 0; +} + +int +sysmount(int fd, char *old, int flags, char *spec) +{ + long r; + Chan *c0 = 0, *bc = 0; + struct { + Chan* chan; + char* spec; + int flags; + } mntparam; + + if(waserror()) { + cclose(bc); + cclose(c0); + return -1; + } + bc = fdtochan(fd, ORDWR, 0, 1); + mntparam.chan = (Chan*)bc; + mntparam.spec = spec; + mntparam.flags = flags; + c0 = (*devtab[devno('M', 0)].attach)(&mntparam); + cclose(bc); + r = bindmount(c0, old, flags, spec); + poperror(); + cclose(c0); + + return r; +} + +int +sysunmount(char *old, char *new) +{ + Chan *cmount = 0, *cmounted = 0; + + if(waserror()) { + cclose(cmount); + cclose(cmounted); + return -1; + } + + cmount = namec(new, Amount, OREAD, 0); + if(old != 0) + cmounted = namec(old, Aopen, OREAD, 0); + + cunmount(cmount, cmounted); + poperror(); + cclose(cmount); + cclose(cmounted); + return 0; +} + +int +sysopen(char *path, int mode) +{ + int fd; + Chan *c = 0; + + if(waserror()){ + cclose(c); + return -1; + } + openmode(mode); /* error check only */ + c = namec(path, Aopen, mode, 0); + fd = newfd((Chan*)c); + poperror(); + return fd; +} + +long +unionread(Chan *c, void *va, long n) +{ + long nr; + Chan *nc = 0; + Pgrp *pg = 0; + + pg = up->pgrp; + rlock(&pg->ns); + + for(;;) { + if(waserror()) { + runlock(&pg->ns); + nexterror(); + } + nc = clone(c->mnt->to, 0); + poperror(); + + if(c->mountid != c->mnt->mountid) { + runlock(&pg->ns); + cclose(nc); + return 0; + } + + /* Error causes component of union to be skipped */ + if(waserror()) { + cclose(nc); + goto next; + } + + nc = (*devtab[nc->type].open)((Chan*)nc, OREAD); + nc->offset = c->offset; + nr = (*devtab[nc->type].read)((Chan*)nc, va, n, nc->offset); + /* devdirread e.g. changes it */ + c->offset = nc->offset; + poperror(); + + cclose(nc); + if(nr > 0) { + runlock(&pg->ns); + return nr; + } + /* Advance to next element */ + next: + c->mnt = c->mnt->next; + if(c->mnt == 0) + break; + c->mountid = c->mnt->mountid; + c->offset = 0; + } + runlock(&pg->ns); + return 0; +} + +long +sysread(int fd, void *va, long n) +{ + int dir; + Lock *cl; + Chan *c = 0; + + if(waserror()) { + cclose(c); + return -1; + } + c = fdtochan(fd, OREAD, 1, 1); + + dir = c->qid.path&CHDIR; + if(dir) { + n -= n%DIRLEN; + if(c->offset%DIRLEN || n==0) + error(Etoosmall); + } + + if(dir && c->mnt) + n = unionread((Chan*)c, va, n); + else + n = (*devtab[c->type].read)((Chan*)c, va, n, c->offset); + + cl = (Lock*)&c->r.l; + lock(cl); + c->offset += n; + unlock(cl); + + poperror(); + cclose(c); + + return n; +} + +int +sysremove(char *path) +{ + Chan *c = 0; + + if(waserror()) { + if(c != 0) + c->type = 0; /* see below */ + cclose(c); + return -1; + } + c = namec(path, Aaccess, 0, 0); + (*devtab[c->type].remove)((Chan*)c); + /* + * Remove clunks the fid, but we need to recover the Chan + * so fake it up. rootclose() is known to be a nop. + */ + c->type = 0; + poperror(); + cclose(c); + return 0; +} + +long +sysseek(int fd, long off, int whence) +{ + Dir dir; + Chan *c; + char buf[DIRLEN]; + + if(waserror()) + return -1; + + c = fdtochan(fd, -1, 1, 0); + if(c->qid.path & CHDIR) + error(Eisdir); + + switch(whence) { + case 0: + c->offset = off; + break; + + case 1: + lock(&c->r.l); /* lock for read/write update */ + c->offset += off; + off = c->offset; + unlock(&c->r.l); + break; + + case 2: + (*devtab[c->type].stat)(c, buf); + convM2D(buf, &dir); + c->offset = dir.length + off; + off = c->offset; + break; + } + poperror(); + return off; +} + +int +sysstat(char *path, char *buf) +{ + Chan *c = 0; + + if(waserror()){ + cclose(c); + return -1; + } + c = namec(path, Aaccess, 0, 0); + (*devtab[c->type].stat)((Chan*)c, buf); + poperror(); + cclose(c); + return 0; +} + +long +syswrite(int fd, void *va, long n) +{ + Lock *cl; + Chan *c = 0; + + if(waserror()) { + cclose(c); + return -1; + } + c = fdtochan(fd, OWRITE, 1, 1); + if(c->qid.path & CHDIR) + error(Eisdir); + + n = (*devtab[c->type].write)((Chan*)c, va, n, c->offset); + + cl = (Lock*)&c->r.l; + lock(cl); + c->offset += n; + unlock(cl); + + poperror(); + cclose(c); + + return n; +} + +int +syswstat(char *path, char *buf) +{ + Chan *c = 0; + + if(waserror()) { + cclose(c); + return -1; + } + + nameok(buf); + c = namec(path, Aaccess, 0, 0); + (*devtab[c->type].wstat)((Chan*)c, buf); + poperror(); + cclose(c); + return 0; +} + +int +sysdirstat(char *name, Dir *dir) +{ + char buf[DIRLEN]; + + if(sysstat(name, buf) == -1) + return -1; + convM2D(buf, dir); + return 0; +} + +int +sysdirfstat(int fd, Dir *dir) +{ + char buf[DIRLEN]; + + if(sysfstat(fd, buf) == -1) + return -1; + + convM2D(buf, dir); + return 0; +} + +int +sysdirwstat(char *name, Dir *dir) +{ + char buf[DIRLEN]; + + convD2M(dir, buf); + return syswstat(name, buf); +} + +int +sysdirfwstat(int fd, Dir *dir) +{ + char buf[DIRLEN]; + + convD2M(dir, buf); + return sysfwstat(fd, buf); +} + +long +sysdirread(int fd, Dir *dbuf, long count) +{ + int c, n, i, r; + char buf[DIRLEN*50]; + + n = 0; + count = (count/sizeof(Dir)) * DIRLEN; + while(n < count) { + c = count - n; + if(c > sizeof(buf)) + c = sizeof(buf); + r = sysread(fd, buf, c); + if(r == 0) + break; + if(r < 0 || r % DIRLEN) + return -1; + for(i=0; i<r; i+=DIRLEN) { + convM2D(buf+i, dbuf); + dbuf++; + } + n += r; + if(r != c) + break; + } + + return (n/DIRLEN) * sizeof(Dir); +} + +static int +call(char *clone, char *dest, int *cfdp, char *dir, char *local) +{ + int fd, cfd, n; + char *p, name[3*NAMELEN+5], data[3*NAMELEN+10]; + + cfd = sysopen(clone, ORDWR); + if(cfd < 0){ + werrstr("%s: %r", clone); + return -1; + } + + /* get directory name */ + n = sysread(cfd, name, sizeof(name)-1); + if(n < 0) { + sysclose(cfd); + return -1; + } + name[n] = 0; + sprint(name, "%d", strtoul(name, 0, 0)); + p = strrchr(clone, '/'); + *p = 0; + if(dir) + snprint(dir, 2*NAMELEN, "%s/%s", clone, name); + snprint(data, sizeof(data), "%s/%s/data", clone, name); + + /* connect */ + /* set local side (port number, for example) if we need to */ + if(local) + snprint(name, sizeof(name), "connect %s %s", dest, local); + else + snprint(name, sizeof(name), "connect %s", dest); + if(syswrite(cfd, name, strlen(name)) < 0){ + werrstr("%s failed: %r", name); + sysclose(cfd); + return -1; + } + + /* open data connection */ + fd = sysopen(data, ORDWR); + if(fd < 0){ + werrstr("can't open %s: %r", data); + sysclose(cfd); + return -1; + } + if(cfdp) + *cfdp = cfd; + else + sysclose(cfd); + return fd; +} + +int +sysdial(char *dest, char *local, char *dir, int *cfdp) +{ + int n, fd, rv; + char *p, net[128], clone[NAMELEN+12]; + + /* go for a standard form net!... */ + p = strchr(dest, '!'); + if(p == 0){ + snprint(net, sizeof(net), "net!%s", dest); + } else { + strncpy(net, dest, sizeof(net)-1); + net[sizeof(net)-1] = 0; + } + + /* call the connection server */ + fd = sysopen("/net/cs", ORDWR); + if(fd < 0){ + /* no connection server, don't translate */ + p = strchr(net, '!'); + *p++ = 0; + snprint(clone, sizeof(clone), "/net/%s/clone", net); + return call(clone, p, cfdp, dir, local); + } + + /* + * send dest to connection to translate + */ + if(syswrite(fd, net, strlen(net)) < 0){ + werrstr("%s: %r", net); + sysclose(fd); + return -1; + } + + /* + * loop through each address from the connection server till + * we get one that works. + */ + rv = -1; + sysseek(fd, 0, 0); + while((n = sysread(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; + } + sysclose(fd); + return rv; +} + +static int +identtrans(char *addr, char *naddr, int na, char *file, int nf) +{ + char *p; + char reply[4*NAMELEN]; + + /* parse the network */ + strncpy(reply, addr, sizeof(reply)); + reply[sizeof(reply)-1] = 0; + p = strchr(reply, '!'); + if(p) + *p++ = 0; + + sprint(file, "/net/%.*s/clone", na - sizeof("/net//clone"), reply); + strncpy(naddr, p, na); + naddr[na-1] = 0; + return 1; +} + +static int +nettrans(char *addr, char *naddr, int na, char *file, int nf) +{ + long n; + int fd; + char *cp; + char reply[4*NAMELEN]; + + /* + * ask the connection server + */ + fd = sysopen("/net/cs", ORDWR); + if(fd < 0) + return identtrans(addr, naddr, na, file, nf); + if(syswrite(fd, addr, strlen(addr)) < 0){ + sysclose(fd); + return -1; + } + sysseek(fd, 0, 0); + n = sysread(fd, reply, sizeof(reply)-1); + sysclose(fd); + if(n <= 0) + return -1; + reply[n] = '\0'; + + /* + * parse the reply + */ + cp = strchr(reply, ' '); + if(cp == 0) + return -1; + *cp++ = 0; + strncpy(naddr, cp, na); + naddr[na-1] = 0; + strncpy(file, reply, nf); + file[nf-1] = 0; + return 0; +} + +int +sysannounce(char *addr, char *dir) +{ + char *cp; + int ctl, n, m; + char buf[3*NAMELEN]; + char buf2[3*NAMELEN]; + char netdir[2*NAMELEN]; + char naddr[3*NAMELEN]; + + /* + * translate the address + */ + if(nettrans(addr, naddr, sizeof(naddr), netdir, sizeof(netdir)) < 0){ + werrstr("can't translate address"); + return -1; + } + + /* + * get a control channel + */ + ctl = sysopen(netdir, ORDWR); + if(ctl<0){ + werrstr("can't open control channel"); + return -1; + } + cp = strrchr(netdir, '/'); + *cp = 0; + + /* + * find out which line we have + */ + n = sprint(buf, "%.*s/", 2*NAMELEN+1, netdir); + m = sysread(ctl, &buf[n], sizeof(buf)-n-1); + if(m <= 0) { + sysclose(ctl); + werrstr("can't read control file"); + return -1; + } + buf[n+m] = 0; + + /* + * make the call + */ + n = sprint(buf2, "announce %.*s", 2*NAMELEN, naddr); + if(syswrite(ctl, buf2, n) != n) { + sysclose(ctl); + werrstr("announcement fails"); + return -1; + } + + strcpy(dir, buf); + + return ctl; +} + +int +syslisten(char *dir, char *newdir) +{ + char *cp; + int ctl, n, m; + char buf[3*NAMELEN]; + + /* + * open listen, wait for a call + */ + sprint(buf, "%.*s/listen", 2*NAMELEN+1, dir); + ctl = sysopen(buf, ORDWR); + if(ctl < 0) + return -1; + + /* + * find out which line we have + */ + strcpy(buf, dir); + cp = strrchr(buf, '/'); + *++cp = 0; + n = cp-buf; + m = sysread(ctl, cp, sizeof(buf) - n - 1); + if(n<=0){ + sysclose(ctl); + return -1; + } + buf[n+m] = 0; + + strcpy(newdir, buf); + return ctl; +} diff --git a/sys/src/cmd/unix/drawterm/kern/waserror.c b/sys/src/cmd/unix/drawterm/kern/waserror.c new file mode 100755 index 000000000..3a07bc928 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/waserror.c @@ -0,0 +1,27 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +Label* +pwaserror(void) +{ + if(up->nerrlab == NERR) + panic("error stack overflow"); + return &up->errlab[up->nerrlab++]; +} + +void +nexterror(void) +{ + longjmp(up->errlab[--up->nerrlab].buf, 1); +} + +void +error(char *e) +{ + kstrcpy(up->errstr, e, ERRMAX); + setjmp(up->errlab[NERR-1].buf); + nexterror(); +} diff --git a/sys/src/cmd/unix/drawterm/kern/win32.c b/sys/src/cmd/unix/drawterm/kern/win32.c new file mode 100755 index 000000000..fa79c1d54 --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/win32.c @@ -0,0 +1,470 @@ +#include <windows.h> +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include <libsec.h> + +typedef struct Oproc Oproc; +struct Oproc { + int tid; + HANDLE *sema; +}; + +static int tlsx = TLS_OUT_OF_INDEXES; + +char *argv0; + +Proc* +_getproc(void) +{ + if(tlsx == TLS_OUT_OF_INDEXES) + return nil; + return TlsGetValue(tlsx); +} + +void +_setproc(Proc *p) +{ + if(tlsx == TLS_OUT_OF_INDEXES){ + tlsx = TlsAlloc(); + if(tlsx == TLS_OUT_OF_INDEXES) + panic("out of indexes"); + } + TlsSetValue(tlsx, p); +} + +void +oserror(void) +{ + oserrstr(); + nexterror(); +} + +void +osinit(void) +{ + Oproc *t; + static Proc firstprocCTstore; + + _setproc(&firstprocCTstore); + t = (Oproc*)firstprocCTstore.oproc; + assert(t != 0); + + t->tid = GetCurrentThreadId(); + t->sema = CreateSemaphore(0, 0, 1000, 0); + if(t->sema == 0) { + oserror(); + panic("could not create semaphore: %r"); + } +} + +void +osnewproc(Proc *p) +{ + Oproc *op; + + op = (Oproc*)p->oproc; + op->sema = CreateSemaphore(0, 0, 1000, 0); + if (op->sema == 0) { + oserror(); + panic("could not create semaphore: %r"); + } +} + +void +osmsleep(int ms) +{ + Sleep((DWORD) ms); +} + +void +osyield(void) +{ + Sleep(0); +} + +static DWORD WINAPI tramp(LPVOID vp); + +void +osproc(Proc *p) +{ + DWORD tid; + + if(CreateThread(0, 0, tramp, p, 0, &tid) == 0) { + oserror(); + panic("osproc: %r"); + } + + Sleep(0); +} + +static DWORD WINAPI +tramp(LPVOID vp) +{ + Proc *p = (Proc *) vp; + Oproc *op = (Oproc*) p->oproc; + + _setproc(p); + op->tid = GetCurrentThreadId(); + op->sema = CreateSemaphore(0, 0, 1000, 0); + if(op->sema == 0) { + oserror(); + panic("could not create semaphore: %r"); + } + + (*p->fn)(p->arg); + ExitThread(0); + return 0; +} + +void +procsleep(void) +{ + Proc *p; + Oproc *op; + + p = up; + op = (Oproc*)p->oproc; + WaitForSingleObject(op->sema, INFINITE);} + +void +procwakeup(Proc *p) +{ + Oproc *op; + + op = (Oproc*)p->oproc; + ReleaseSemaphore(op->sema, 1, 0); +} + +void +random20(uchar *p) +{ + LARGE_INTEGER ti; + int i, j; + FILETIME ft; + DigestState ds; + vlong tsc; + + GetSystemTimeAsFileTime(&ft); + memset(&ds, 0, sizeof ds); + sha1((uchar*)&ft, sizeof(ft), 0, &ds); + for(i=0; i<50; i++) { + for(j=0; j<10; j++) { + QueryPerformanceCounter(&ti); + sha1((uchar*)&ti, sizeof(ti), 0, &ds); + tsc = GetTickCount(); + sha1((uchar*)&tsc, sizeof(tsc), 0, &ds); + } + Sleep(10); + } + sha1(0, 0, p, &ds); +} + +void +randominit(void) +{ +} + +ulong +randomread(void *v, ulong n) +{ + int i; + uchar p[20]; + + for(i=0; i<n; i+=20){ + random20(p); + if(i+20 <= n) + memmove((char*)v+i, p, 20); + else + memmove((char*)v+i, p, n-i); + } + return n; +} + +long +seconds(void) +{ + return time(0); +} + +ulong +ticks(void) +{ + return GetTickCount(); +} + +#if 0 +uvlong +fastticks(uvlong *v) +{ + uvlong n; + + n = GetTickCount() * 1000 * 1000; + if(v) + *v = n; + return n; +} +#endif + +extern int main(int, char*[]); + + +int +wstrutflen(Rune *s) +{ + int n; + + for(n=0; *s; n+=runelen(*s),s++) + ; + return n; +} + +int +wstrtoutf(char *s, Rune *t, int n) +{ + int i; + char *s0; + + s0 = s; + if(n <= 0) + return wstrutflen(t)+1; + while(*t) { + if(n < UTFmax+1 && n < runelen(*t)+1) { + *s = 0; + return s-s0+wstrutflen(t)+1; + } + i = runetochar(s, t); + s += i; + n -= i; + t++; + } + *s = 0; + return s-s0; +} + +int +win_hasunicode(void) +{ + OSVERSIONINFOA osinfo; + int r; + + osinfo.dwOSVersionInfoSize = sizeof(osinfo); + if(!GetVersionExA(&osinfo)) + panic("GetVersionEx failed"); + switch(osinfo.dwPlatformId) { + default: + panic("unknown PlatformId"); + case VER_PLATFORM_WIN32s: + panic("Win32s not supported"); + case VER_PLATFORM_WIN32_WINDOWS: + r = 0; + break; + case VER_PLATFORM_WIN32_NT: + r = 1; + break; + } + + return r; +} + +int +wstrlen(Rune *s) +{ + int n; + + for(n=0; *s; s++,n++) + ; + return n; +} +static int args(char *argv[], int n, char *p); + +int APIENTRY +WinMain(HINSTANCE x, HINSTANCE y, LPSTR z, int w) +{ + int argc, n; + char *arg, *p, **argv; + Rune *warg; + + if(0 && win_hasunicode()){ + warg = GetCommandLineW(); + n = (wstrlen(warg)+1)*UTFmax; + arg = malloc(n); + wstrtoutf(arg, warg, n); + }else + arg = GetCommandLineA(); + + /* conservative guess at the number of args */ + for(argc=4,p=arg; *p; p++) + if(*p == ' ' || *p == '\t') + argc++; + argv = malloc(argc*sizeof(char*)); + argc = args(argv, argc, arg); + + mymain(argc, argv); + ExitThread(0); + return 0; +} + +/* + * Break the command line into arguments + * The rules for this are not documented but appear to be the following + * according to the source for the microsoft C library. + * Words are seperated by space or tab + * Words containing a space or tab can be quoted using " + * 2N backslashes + " ==> N backslashes and end quote + * 2N+1 backslashes + " ==> N backslashes + literal " + * N backslashes not followed by " ==> N backslashes + */ +static int +args(char *argv[], int n, char *p) +{ + char *p2; + int i, j, quote, nbs; + + for(i=0; *p && i<n-1; i++) { + while(*p == ' ' || *p == '\t') + p++; + quote = 0; + argv[i] = p2 = p; + for(;*p; p++) { + if(!quote && (*p == ' ' || *p == '\t')) + break; + for(nbs=0; *p == '\\'; p++,nbs++) + ; + if(*p == '"') { + for(j=0; j<(nbs>>1); j++) + *p2++ = '\\'; + if(nbs&1) + *p2++ = *p; + else + quote = !quote; + } else { + for(j=0; j<nbs; j++) + *p2++ = '\\'; + *p2++ = *p; + } + } + /* move p up one to avoid pointing to null at end of p2 */ + if(*p) + p++; + *p2 = 0; + } + argv[i] = 0; + + return i; +} +/* + * Windows socket error messages + * There must be a way to get these strings out of the library. + * This table is derived from the MSDN online help. + */ +static struct { + int e; + char *s; +} tab[] = { + { 10004, "interrupted function call" }, + { 10013, "permission denied" }, + { 10014, "bad address" }, + { 10022, "invalid argument" }, + { 10024, "too many open files" }, + { 10035, "resource temporarily unavailable" }, + { 10036, "operation now in progress" }, + { 10037, "operation already in progress" }, + { 10038, "socket operation on nonsocket" }, + { 10039, "destination address required" }, + { 10040, "message too long" }, + { 10041, "protocol wrong type for socket" }, + { 10042, "bad protocol option" }, + { 10043, "protocol not supported" }, + { 10044, "socket type not supported" }, + { 10045, "operation not supported" }, + { 10046, "protocol family not supported" }, + { 10047, "address family not supported by protocol family" }, + { 10048, "address already in use" }, + { 10049, "cannot assign requested address" }, + { 10050, "network is down" }, + { 10051, "network is unreachable" }, + { 10052, "network dropped connection on reset" }, + { 10053, "software caused connection abort" }, + { 10054, "connection reset by peer" }, + { 10055, "no buffer space available" }, + { 10056, "socket is already connected" }, + { 10057, "socket is not connected" }, + { 10058, "cannot send after socket shutdown" }, + { 10060, "connection timed out" }, + { 10061, "connection refused" }, + { 10064, "host is down" }, + { 10065, "no route to host" }, + { 10067, "too many processes" }, + { 10091, "network subsystem is unavailable" }, + { 10092, "winsock.dll version out of range" }, + { 10093, "wsastartup not called" }, + { 10101, "graceful shutdown in progress" }, + { 10109, "class type not found" }, + { 11001, "host name not found" }, + { 11002, "host not found (non-authoritative)" }, + { 11003, "nonrecoverable error" }, + { 11004, "valid name, but no data record of requested type" }, +}; + +void +osrerrstr(char *buf, uint nbuf) +{ + char *p, *q; + int e, i, r; + + e = GetLastError(); + r = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, + 0, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + buf, nbuf, 0); + if(r == 0){ + for(i=0; i<nelem(tab); i++) + if(tab[i].e == e){ + strecpy(buf, buf+nbuf, tab[i].s); + break; + } + if(i==nelem(tab)) + snprint(buf, nbuf, "windows error %d", e); + } + + for(p=q=buf; *p; p++) { + if(*p == '\r') + continue; + if(*p == '\n') + *q++ = ' '; + else + *q++ = *p; + } + *q = '\0'; +} + +void +oserrstr(void) +{ + osrerrstr(up->errstr, ERRMAX); +} + +long +showfilewrite(char *a, int n) +{ + Rune *action, *arg, *cmd; + static Rune Lopen[] = { 'o', 'p', 'e', 'n', 0 }; + + cmd = runesmprint("%.*s", n, a); + if(cmd == nil) + error("out of memory"); + if(cmd[runestrlen(cmd)-1] == '\n') + cmd[runestrlen(cmd)] = 0; + p = runestrchr(cmd, ' '); + if(p){ + action = cmd; + *p++ = 0; + arg = p; + }else{ + action = Lopen; + arg = cmd; + } + ShellExecute(0, 0, action, arg, 0, SW_SHOWNORMAL); + return n; +} |