diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
| commit | 37da2899f40661e3e9631e497da8dc59b971cbd0 (patch) | |
| tree | cbc6d4680e347d906f5fa7fca73214418741df72 /emu/port | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'emu/port')
77 files changed, 37095 insertions, 0 deletions
diff --git a/emu/port/acme-offset b/emu/port/acme-offset new file mode 100644 index 00000000..4f083732 --- /dev/null +++ b/emu/port/acme-offset @@ -0,0 +1 @@ +X ,x/b?(read|write)\(.*ulong.*\)$/v/(bread|bwrite)/x/ulong offset/c/vlong offset diff --git a/emu/port/alloc.c b/emu/port/alloc.c new file mode 100644 index 00000000..e2206c3f --- /dev/null +++ b/emu/port/alloc.c @@ -0,0 +1,1021 @@ +#include "dat.h" +#include "fns.h" +#include "interp.h" +#include "error.h" + +enum +{ + MAXPOOL = 4 +}; + +#define left u.s.bhl +#define right u.s.bhr +#define fwd u.s.bhf +#define prev u.s.bhv +#define parent u.s.bhp + +#define RESERVED 512*1024 + +struct Pool +{ + char* name; + int pnum; + ulong maxsize; + int quanta; + int chunk; + int monitor; + ulong ressize; /* restricted size */ + ulong cursize; + ulong arenasize; + ulong hw; + Lock l; + Bhdr* root; + Bhdr* chain; + ulong nalloc; + ulong nfree; + int nbrk; + int lastfree; + void (*move)(void*, void*); +}; + +void* initbrk(ulong); + +struct +{ + int n; + Pool pool[MAXPOOL]; + /* Lock l; */ +} table = { + 3, + { + { "main", 0, 32*1024*1024, 31, 512*1024, 0, 31*1024*1024 }, + { "heap", 1, 32*1024*1024, 31, 512*1024, 0, 31*1024*1024 }, + { "image", 2, 32*1024*1024+256, 31, 4*1024*1024, 1, 31*1024*1024 }, + } +}; + +Pool* mainmem = &table.pool[0]; +Pool* heapmem = &table.pool[1]; +Pool* imagmem = &table.pool[2]; + +static void _auditmemloc(char *, void *); +void (*auditmemloc)(char *, void *) = _auditmemloc; +static void _poolfault(void *, char *, ulong); +void (*poolfault)(void *, char *, ulong) = _poolfault; + +/* non tracing + * +enum { + Npadlong = 0, + MallocOffset = 0, + ReallocOffset = 0 +}; + * + */ + +/* tracing */ +enum { + Npadlong = 2, + MallocOffset = 0, + ReallocOffset = 1 +}; + +enum { + Monitor = 1 +}; + +void (*memmonitor)(int, ulong, ulong, ulong) = nil; +#define MM(v,pc,base,size) if(!Monitor || memmonitor==nil){} else memmonitor((v),(pc),(base),(size)) + +#define CKLEAK 0 +int ckleak; +#define ML(v, sz, pc) if(CKLEAK && ckleak && v){ if(sz) fprint(2, "%lux %lux %lux\n", (ulong)v, (ulong)sz, (ulong)pc); else fprint(2, "%lux\n", (ulong)v); } + +int +memusehigh(void) +{ + return mainmem->cursize > mainmem->ressize || + heapmem->cursize > heapmem->ressize || + 0 && imagmem->cursize > imagmem->ressize; +} + +int +memlow(void) +{ + return heapmem->cursize > (heapmem->maxsize)/2; +} + +int +poolsetsize(char *s, int size) +{ + int i; + + for(i = 0; i < table.n; i++) { + if(strcmp(table.pool[i].name, s) == 0) { + table.pool[i].maxsize = size; + table.pool[i].ressize = size-RESERVED; + if(size < RESERVED) + panic("not enough memory"); + return 1; + } + } + return 0; +} + +void +poolimmutable(void *v) +{ + Bhdr *b; + + D2B(b, v); + b->magic = MAGIC_I; +} + +void +poolmutable(void *v) +{ + Bhdr *b; + + D2B(b, v); + b->magic = MAGIC_A; + ((Heap*)v)->color = mutator; +} + +char* +poolname(Pool *p) +{ + return p->name; +} + +Bhdr* +poolchain(Pool *p) +{ + return p->chain; +} + +void +pooldel(Pool *p, Bhdr *t) +{ + Bhdr *s, *f, *rp, *q; + + if(t->parent == nil && p->root != t) { + t->prev->fwd = t->fwd; + t->fwd->prev = t->prev; + return; + } + + if(t->fwd != t) { + f = t->fwd; + s = t->parent; + f->parent = s; + if(s == nil) + p->root = f; + else { + if(s->left == t) + s->left = f; + else + s->right = f; + } + + rp = t->left; + f->left = rp; + if(rp != nil) + rp->parent = f; + rp = t->right; + f->right = rp; + if(rp != nil) + rp->parent = f; + + t->prev->fwd = t->fwd; + t->fwd->prev = t->prev; + return; + } + + if(t->left == nil) + rp = t->right; + else { + if(t->right == nil) + rp = t->left; + else { + f = t; + rp = t->right; + s = rp->left; + while(s != nil) { + f = rp; + rp = s; + s = rp->left; + } + if(f != t) { + s = rp->right; + f->left = s; + if(s != nil) + s->parent = f; + s = t->right; + rp->right = s; + if(s != nil) + s->parent = rp; + } + s = t->left; + rp->left = s; + s->parent = rp; + } + } + q = t->parent; + if(q == nil) + p->root = rp; + else { + if(t == q->left) + q->left = rp; + else + q->right = rp; + } + if(rp != nil) + rp->parent = q; +} + +void +pooladd(Pool *p, Bhdr *q) +{ + int size; + Bhdr *tp, *t; + + q->magic = MAGIC_F; + + q->left = nil; + q->right = nil; + q->parent = nil; + q->fwd = q; + q->prev = q; + + t = p->root; + if(t == nil) { + p->root = q; + return; + } + + size = q->size; + + tp = nil; + while(t != nil) { + if(size == t->size) { + q->prev = t->prev; + q->prev->fwd = q; + q->fwd = t; + t->prev = q; + return; + } + tp = t; + if(size < t->size) + t = t->left; + else + t = t->right; + } + + q->parent = tp; + if(size < tp->size) + tp->left = q; + else + tp->right = q; +} + +static void* +dopoolalloc(Pool *p, ulong asize, ulong pc) +{ + Bhdr *q, *t; + int alloc, ldr, ns, frag; + int osize, size; + + if(asize >= 1024*1024*1024) /* for sanity and to avoid overflow */ + return nil; + size = asize; + osize = size; + size = (size + BHDRSIZE + p->quanta) & ~(p->quanta); + + lock(&p->l); + p->nalloc++; + + t = p->root; + q = nil; + while(t) { + if(t->size == size) { + t = t->fwd; + pooldel(p, t); + t->magic = MAGIC_A; + p->cursize += t->size; + if(p->cursize > p->hw) + p->hw = p->cursize; + unlock(&p->l); + if(p->monitor) + MM(p->pnum, pc, (ulong)B2D(t), size); + return B2D(t); + } + if(size < t->size) { + q = t; + t = t->left; + } + else + t = t->right; + } + if(q != nil) { + pooldel(p, q); + q->magic = MAGIC_A; + frag = q->size - size; + if(frag < (size>>2) && frag < 0x8000) { + p->cursize += q->size; + if(p->cursize > p->hw) + p->hw = p->cursize; + unlock(&p->l); + if(p->monitor) + MM(p->pnum, pc, (ulong)B2D(q), size); + return B2D(q); + } + /* Split */ + ns = q->size - size; + q->size = size; + B2T(q)->hdr = q; + t = B2NB(q); + t->size = ns; + B2T(t)->hdr = t; + pooladd(p, t); + p->cursize += q->size; + if(p->cursize > p->hw) + p->hw = p->cursize; + unlock(&p->l); + if(p->monitor) + MM(p->pnum, pc, (ulong)B2D(q), size); + return B2D(q); + } + + ns = p->chunk; + if(size > ns) + ns = size; + ldr = p->quanta+1; + + alloc = ns+ldr+ldr; + p->arenasize += alloc; + if(p->arenasize > p->maxsize) { + p->arenasize -= alloc; + ns = p->maxsize-p->arenasize-ldr-ldr; + ns &= ~p->quanta; + if (ns < size) { + if(poolcompact(p)) { + unlock(&p->l); + return poolalloc(p, osize); + } + + unlock(&p->l); + print("arena %s too large: size %d cursize %lud arenasize %lud maxsize %lud\n", + p->name, size, p->cursize, p->arenasize, p->maxsize); + return nil; + } + alloc = ns+ldr+ldr; + p->arenasize += alloc; + } + + p->nbrk++; + t = (Bhdr *)sbrk(alloc); + if(t == (void*)-1) { + p->nbrk--; + unlock(&p->l); + return nil; + } + /* Double alignment */ + t = (Bhdr *)(((ulong)t + 7) & ~7); + + if(p->chain != nil && (char*)t-(char*)B2LIMIT(p->chain)-ldr == 0){ + /* can merge chains */ + if(0)print("merging chains %p and %p in %s\n", p->chain, t, p->name); + q = B2LIMIT(p->chain); + q->magic = MAGIC_A; + q->size = alloc; + B2T(q)->hdr = q; + t = B2NB(q); + t->magic = MAGIC_E; + p->chain->csize += alloc; + p->cursize += alloc; + unlock(&p->l); + poolfree(p, B2D(q)); /* for backward merge */ + return poolalloc(p, osize); + } + + t->magic = MAGIC_E; /* Make a leader */ + t->size = ldr; + t->csize = ns+ldr; + t->clink = p->chain; + p->chain = t; + B2T(t)->hdr = t; + t = B2NB(t); + + t->magic = MAGIC_A; /* Make the block we are going to return */ + t->size = size; + B2T(t)->hdr = t; + q = t; + + ns -= size; /* Free the rest */ + if(ns > 0) { + q = B2NB(t); + q->size = ns; + B2T(q)->hdr = q; + pooladd(p, q); + } + B2NB(q)->magic = MAGIC_E; /* Mark the end of the chunk */ + + p->cursize += t->size; + if(p->cursize > p->hw) + p->hw = p->cursize; + unlock(&p->l); + if(p->monitor) + MM(p->pnum, pc, (ulong)B2D(t), size); + return B2D(t); +} + +void * +poolalloc(Pool *p, ulong asize) +{ + Prog *prog; + + if(p->cursize > p->ressize && (prog = currun()) != nil && prog->flags&Prestricted) + return nil; + return dopoolalloc(p, asize, getcallerpc(&p)); +} + +void +poolfree(Pool *p, void *v) +{ + Bhdr *b, *c; + extern Bhdr *ptr; + + D2B(b, v); + if(p->monitor) + MM(p->pnum|(1<<8), getcallerpc(&p), (ulong)v, b->size); + + lock(&p->l); + p->nfree++; + p->cursize -= b->size; + c = B2NB(b); + if(c->magic == MAGIC_F) { /* Join forward */ + if(c == ptr) + ptr = b; + pooldel(p, c); + c->magic = 0; + b->size += c->size; + B2T(b)->hdr = b; + } + + c = B2PT(b)->hdr; + if(c->magic == MAGIC_F) { /* Join backward */ + if(b == ptr) + ptr = c; + pooldel(p, c); + b->magic = 0; + c->size += b->size; + b = c; + B2T(b)->hdr = b; + } + pooladd(p, b); + unlock(&p->l); +} + +void * +poolrealloc(Pool *p, void *v, ulong size) +{ + Bhdr *b; + void *nv; + int osize; + + if(size >= 1024*1024*1024) /* for sanity and to avoid overflow */ + return nil; + if(size == 0){ + poolfree(p, v); + return nil; + } + SET(osize); + if(v != nil){ + lock(&p->l); + D2B(b, v); + osize = b->size - BHDRSIZE; + unlock(&p->l); + if(osize >= size) + return v; + } + nv = poolalloc(p, size); + if(nv != nil && v != nil){ + memmove(nv, v, osize); + poolfree(p, v); + } + return nv; +} + +ulong +poolmsize(Pool *p, void *v) +{ + Bhdr *b; + ulong size; + + if(v == nil) + return 0; + lock(&p->l); + D2B(b, v); + size = b->size - BHDRSIZE; + unlock(&p->l); + return size; +} + +static ulong +poolmax(Pool *p) +{ + Bhdr *t; + ulong size; + + lock(&p->l); + size = p->maxsize - p->cursize; + t = p->root; + if(t != nil) { + while(t->right != nil) + t = t->right; + if(size < t->size) + size = t->size; + } + if(size >= BHDRSIZE) + size -= BHDRSIZE; + unlock(&p->l); + return size; +} + +ulong +poolmaxsize(void) +{ + int i; + ulong total; + + total = 0; + for(i = 0; i < nelem(table.pool); i++) + total += table.pool[i].maxsize; + return total; +} + +int +poolread(char *va, int count, ulong offset) +{ + Pool *p; + int n, i, signed_off; + + n = 0; + signed_off = offset; + for(i = 0; i < table.n; i++) { + p = &table.pool[i]; + n += snprint(va+n, count-n, "%11lud %11lud %11lud %11lud %11lud %11d %11lud %s\n", + p->cursize, + p->maxsize, + p->hw, + p->nalloc, + p->nfree, + p->nbrk, + poolmax(p), + p->name); + + if(signed_off > 0) { + signed_off -= n; + if(signed_off < 0) { + memmove(va, va+n+signed_off, -signed_off); + n = -signed_off; + } + else + n = 0; + } + + } + return n; +} + +void* +smalloc(size_t size) +{ + void *v; + + for(;;){ + v = malloc(size); + if(v != nil) + break; + if(0) + print("smalloc waiting from %lux\n", getcallerpc(&size)); + osenter(); + osmillisleep(100); + osleave(); + } + setmalloctag(v, getcallerpc(&size)); + setrealloctag(v, 0); + return v; +} + +void* +kmalloc(size_t size) +{ + void *v; + + v = dopoolalloc(mainmem, size+Npadlong*sizeof(ulong), getcallerpc(&size)); + if(v != nil){ + ML(v, size, getcallerpc(&size)); + if(Npadlong){ + v = (ulong*)v+Npadlong; + setmalloctag(v, getcallerpc(&size)); + setrealloctag(v, 0); + } + memset(v, 0, size); + MM(0, getcallerpc(&size), (ulong)v, size); + } + return v; +} + + + +void* +malloc(size_t size) +{ + void *v; + + v = poolalloc(mainmem, size+Npadlong*sizeof(ulong)); + if(v != nil){ + ML(v, size, getcallerpc(&size)); + if(Npadlong){ + v = (ulong*)v+Npadlong; + setmalloctag(v, getcallerpc(&size)); + setrealloctag(v, 0); + } + memset(v, 0, size); + MM(0, getcallerpc(&size), (ulong)v, size); + } else + print("malloc failed from %lux\n", getcallerpc(&size)); + return v; +} + +void* +mallocz(ulong size, int clr) +{ + void *v; + + v = poolalloc(mainmem, size+Npadlong*sizeof(ulong)); + if(v != nil){ + ML(v, size, getcallerpc(&size)); + if(Npadlong){ + v = (ulong*)v+Npadlong; + setmalloctag(v, getcallerpc(&size)); + setrealloctag(v, 0); + } + if(clr) + memset(v, 0, size); + MM(0, getcallerpc(&size), (ulong)v, size); + } else + print("mallocz failed from %lux\n", getcallerpc(&size)); + return v; +} + +void +free(void *v) +{ + Bhdr *b; + + if(v != nil) { + if(Npadlong) + v = (ulong*)v-Npadlong; + D2B(b, v); + ML(v, 0, 0); + MM(1<<8|0, getcallerpc(&v), (ulong)((ulong*)v+Npadlong), b->size); + poolfree(mainmem, v); + } +} + +void* +realloc(void *v, size_t size) +{ + void *nv; + + if(size == 0) + return malloc(size); /* temporary change until realloc calls can be checked */ + if(v != nil) + v = (ulong*)v-Npadlong; + if(Npadlong!=0 && size!=0) + size += Npadlong*sizeof(ulong); + nv = poolrealloc(mainmem, v, size); + ML(v, 0, 0); + ML(nv, size, getcallerpc(&v)); + if(nv != nil) { + nv = (ulong*)nv+Npadlong; + setrealloctag(nv, getcallerpc(&v)); + if(v == nil) + setmalloctag(v, getcallerpc(&v)); + } else + print("realloc failed from %lux\n", getcallerpc(&v)); + return nv; +} + +void +setmalloctag(void *v, ulong pc) +{ + ulong *u; + + USED(v); + USED(pc); + if(Npadlong <= MallocOffset || v == nil) + return; + u = v; + u[-Npadlong+MallocOffset] = pc; +} + +ulong +getmalloctag(void *v) +{ + USED(v); + if(Npadlong <= MallocOffset) + return ~0; + return ((ulong*)v)[-Npadlong+MallocOffset]; +} + +void +setrealloctag(void *v, ulong pc) +{ + ulong *u; + + USED(v); + USED(pc); + if(Npadlong <= ReallocOffset || v == nil) + return; + u = v; + u[-Npadlong+ReallocOffset] = pc; +} + +ulong +getrealloctag(void *v) +{ + USED(v); + if(Npadlong <= ReallocOffset) + return ((ulong*)v)[-Npadlong+ReallocOffset]; + return ~0; +} + +ulong +msize(void *v) +{ + if(v == nil) + return 0; + return poolmsize(mainmem, (ulong*)v-Npadlong)-Npadlong*sizeof(ulong); +} + +void* +calloc(size_t n, size_t szelem) +{ + return malloc(n*szelem); +} + +/* +void +pooldump(Pool *p) +{ + Bhdr *b, *base, *limit, *ptr; + + b = p->chain; + if(b == nil) + return; + base = b; + ptr = b; + limit = B2LIMIT(b); + + while(base != nil) { + print("\tbase #%.8lux ptr #%.8lux", base, ptr); + if(ptr->magic == MAGIC_A || ptr->magic == MAGIC_I) + print("\tA%.5d\n", ptr->size); + else if(ptr->magic == MAGIC_E) + print("\tE\tL#%.8lux\tS#%.8lux\n", ptr->clink, ptr->csize); + else + print("\tF%.5d\tL#%.8lux\tR#%.8lux\tF#%.8lux\tP#%.8lux\tT#%.8lux\n", + ptr->size, ptr->left, ptr->right, ptr->fwd, ptr->prev, ptr->parent); + ptr = B2NB(ptr); + if(ptr >= limit) { + print("link to #%.8lux\n", base->clink); + base = base->clink; + if(base == nil) + break; + ptr = base; + limit = B2LIMIT(base); + } + } +} +*/ + +void +poolsetcompact(Pool *p, void (*move)(void*, void*)) +{ + p->move = move; +} + +int +poolcompact(Pool *pool) +{ + Bhdr *base, *limit, *ptr, *end, *next; + int compacted, nb; + + if(pool->move == nil || pool->lastfree == pool->nfree) + return 0; + + pool->lastfree = pool->nfree; + + base = pool->chain; + ptr = B2NB(base); /* First Block in arena has clink */ + limit = B2LIMIT(base); + compacted = 0; + + pool->root = nil; + end = ptr; + while(base != nil) { + next = B2NB(ptr); + if(ptr->magic == MAGIC_A || ptr->magic == MAGIC_I) { + if(ptr != end) { + memmove(end, ptr, ptr->size); + pool->move(B2D(ptr), B2D(end)); + compacted = 1; + } + end = B2NB(end); + } + if(next >= limit) { + nb = (uchar*)limit - (uchar*)end; + if(nb > 0){ + if(nb < pool->quanta+1){ + print("poolcompact: leftover too small\n"); + abort(); + } + end->size = nb; + B2T(end)->hdr = end; + pooladd(pool, end); + } + base = base->clink; + if(base == nil) + break; + ptr = B2NB(base); + end = ptr; /* could do better by copying between chains */ + limit = B2LIMIT(base); + } else + ptr = next; + } + + return compacted; +} + +static void +_poolfault(void *v, char *msg, ulong c) +{ + auditmemloc(msg, v); + panic("%s %lux (from %lux/%lux)", msg, v, getcallerpc(&v), c); +} + +static void +dumpvl(char *msg, ulong *v, int n) +{ + int i, l; + + l = print("%s at %p: ", msg, v); + for (i = 0; i < n; i++) { + if (l >= 60) { + print("\n"); + l = print(" %p: ", v); + } + l += print(" %lux", *v++); + } + print("\n"); +} + +static void +corrupted(char *str, char *msg, Pool *p, Bhdr *b, void *v) +{ + print("%s(%p): pool %s CORRUPT: %s at %p'%lud(magic=%lux)\n", + str, v, p->name, msg, b, b->size, b->magic); + dumpvl("bad Bhdr", (ulong *)((ulong)b & ~3)-4, 10); +} + +static void +_auditmemloc(char *str, void *v) +{ + Pool *p; + Bhdr *bc, *ec, *b, *nb, *fb = nil; + char *fmsg, *msg; + ulong fsz; + + SET(fsz); + SET(fmsg); + for (p = &table.pool[0]; p < &table.pool[nelem(table.pool)]; p++) { + lock(&p->l); + for (bc = p->chain; bc != nil; bc = bc->clink) { + if (bc->magic != MAGIC_E) { + unlock(&p->l); + corrupted(str, "chain hdr!=MAGIC_E", p, bc, v); + goto nextpool; + } + ec = B2LIMIT(bc); + if (((Bhdr*)v >= bc) && ((Bhdr*)v < ec)) + goto found; + } + unlock(&p->l); +nextpool: ; + } + print("%s: %p not in pools\n", str, v); + return; + +found: + for (b = bc; b < ec; b = nb) { + switch(b->magic) { + case MAGIC_F: + msg = "free blk"; + break; + case MAGIC_I: + msg = "immutable block"; + break; + case MAGIC_A: + msg = "block"; + break; + default: + if (b == bc && b->magic == MAGIC_E) { + msg = "pool hdr"; + break; + } + unlock(&p->l); + corrupted(str, "bad magic", p, b, v); + goto badchunk; + } + if (b->size <= 0 || (b->size & p->quanta)) { + unlock(&p->l); + corrupted(str, "bad size", p, b, v); + goto badchunk; + } + if (fb != nil) + break; + nb = B2NB(b); + if ((Bhdr*)v < nb) { + fb = b; + fsz = b->size; + fmsg = msg; + } + } + unlock(&p->l); + if (b >= ec) { + if (b > ec) + corrupted(str, "chain size mismatch", p, b, v); + else if (b->magic != MAGIC_E) + corrupted(str, "chain end!=MAGIC_E", p, b, v); + } +badchunk: + if (fb != nil) { + print("%s: %p in %s:", str, v, p->name); + if (fb == v) + print(" is %s '%lux\n", fmsg, fsz); + else + print(" in %s at %p'%lux\n", fmsg, fb, fsz); + dumpvl("area", (ulong *)((ulong)v & ~3)-4, 20); + } +} + +char * +poolaudit(char*(*audit)(int, Bhdr *)) +{ + Pool *p; + Bhdr *bc, *ec, *b; + char *r = nil; + + for (p = &table.pool[0]; p < &table.pool[nelem(table.pool)]; p++) { + lock(&p->l); + for (bc = p->chain; bc != nil; bc = bc->clink) { + if (bc->magic != MAGIC_E) { + unlock(&p->l); + return "bad chain hdr"; + } + ec = B2LIMIT(bc); + for (b = bc; b < ec; b = B2NB(b)) { + if (b->size <= 0 || (b->size & p->quanta)) + r = "bad size in bhdr"; + else + switch(b->magic) { + case MAGIC_E: + if (b != bc) { + r = "unexpected MAGIC_E"; + break; + } + case MAGIC_F: + case MAGIC_A: + case MAGIC_I: + r = audit(p->pnum, b); + break; + default: + r = "bad magic"; + } + if (r != nil) { + unlock(&p->l); + return r; + } + } + if (b != ec || b->magic != MAGIC_E) { + unlock(&p->l); + return "bad chain ending"; + } + } + unlock(&p->l); + } + return r; +} diff --git a/emu/port/audio-tbls.c b/emu/port/audio-tbls.c new file mode 100644 index 00000000..ed84bec6 --- /dev/null +++ b/emu/port/audio-tbls.c @@ -0,0 +1,53 @@ +svp_t audio_bits_tbl[] = { + { "8", 8 } , /* 8 bits per sample */ + { "16", 16 }, /* 16 bits per sample */ + {nil}, +}; + +svp_t audio_chan_tbl[] = { + { "1", 1 }, /* 1 channel */ + { "2", 2 }, /* 2 channels */ + {nil}, +}; + +svp_t audio_indev_tbl[] = { + { "mic", Audio_Mic_Val }, /* input microphone */ + { "line", Audio_Linein_Val }, /* line in */ + {nil}, +}; + +svp_t audio_outdev_tbl[] = { + { "spkr", Audio_Speaker_Val }, /* output speaker */ + { "hdph", Audio_Headphone_Val },/* head phones */ + { "line", Audio_Lineout_Val }, /* line out */ + {nil}, +}; + +svp_t audio_enc_tbl[] = { + { "ulaw", Audio_Ulaw_Val }, /* u-law encoding */ + { "alaw", Audio_Alaw_Val }, /* A-law encoding */ + { "pcm", Audio_Pcm_Val }, /* Pulse Code Modulation */ + {nil}, +}; + +svp_t audio_rate_tbl[] = { + { "8000", 8000 }, /* 8000 samples per second */ + { "11025", 11025 }, /* 11025 samples per second */ + { "22050", 22050 }, /* 22050 samples per second */ + { "44100", 44100 }, /* 44100 samples per second */ + {nil}, +}; + +Audio_d Default_Audio_Format = { + 0, + 16, /* bits per sample */ + Audio_Max_Val, /* buffer size (as percentage) */ + 2, /* number of channels */ + -1, /* device */ + Audio_Pcm_Val, /* encoding format */ + 8000, /* samples per second */ + Audio_Max_Val, /* left channel gain */ + Audio_Max_Val, /* right channel gain */ +}; +int Default_Audio_Input = Audio_Mic_Val; +int Default_Audio_Output = Audio_Speaker_Val; diff --git a/emu/port/audio.h b/emu/port/audio.h new file mode 100644 index 00000000..5846cda8 --- /dev/null +++ b/emu/port/audio.h @@ -0,0 +1,81 @@ +#define AUDIO_BITS_FLAG 0x00000001 +#define AUDIO_BUF_FLAG 0x00000002 +#define AUDIO_CHAN_FLAG 0x00000004 +#define AUDIO_COUNT_FLAG 0x00000008 +#define AUDIO_DEV_FLAG 0x00000010 +#define AUDIO_ENC_FLAG 0x00000020 +#define AUDIO_RATE_FLAG 0x00000040 +#define AUDIO_VOL_FLAG 0x00000080 +#define AUDIO_LEFT_FLAG 0x00000100 +#define AUDIO_RIGHT_FLAG 0x00000200 +#define AUDIO_IN_FLAG 0x00000400 +#define AUDIO_OUT_FLAG 0x00000800 +#define AUDIO_MOD_FLAG 0x10000000 + +#define Audio_Min_Val 0 +#define Audio_Max_Val 100 + +#define Audio_No_Val 0 +#define Audio_In_Val 1 +#define Audio_Out_Val 2 + +#define Audio_Max_Buf 32768 +#define Bits_Per_Byte 8 + +typedef struct Audio_d Audio_d; +struct Audio_d { + ulong flags; /* bit flag for fields */ + ulong bits; /* bits per sample */ + ulong buf; /* buffer size */ + ulong chan; /* number of channels */ + ulong dev; /* device */ + ulong enc; /* encoding format */ + ulong rate; /* samples per second */ + ulong left; /* left channel gain */ + ulong right; /* right channel gain */ +}; + +typedef struct Audio_t Audio_t; +struct Audio_t { + Audio_d in; /* input device */ + Audio_d out; /* output device */ +}; + +#define AUDIO_CMD_MAXNUM 32 + +void audio_info_init(Audio_t*); +int audioparse(char*, int n, Audio_t*); + +enum +{ + Qdir = 0, /* must start at 0 representing a directory */ + Qaudio, + Qaudioctl +}; + +/* required external platform specific functions */ +void audio_file_init(void); +void audio_file_open(Chan*, int); +long audio_file_read(Chan*, void*, long, vlong); +long audio_file_write(Chan*, void*, long, vlong); +long audio_ctl_write(Chan*, void*, long, vlong); +void audio_file_close(Chan*); + +typedef struct _svp_t { + char* s; /* string */ + unsigned long v; /* value */ +} svp_t; + +/* string value pairs for default audio values */ +extern svp_t audio_chan_tbl[]; +extern svp_t audio_indev_tbl[]; +extern svp_t audio_outdev_tbl[]; +extern svp_t audio_enc_tbl[]; +extern svp_t audio_rate_tbl[]; +extern svp_t audio_val_tbl[]; +extern svp_t audio_bits_tbl[]; + +extern Audio_d Default_Audio_Format; +extern int Default_Audio_Input, Default_Audio_Output; + +extern Audio_t* getaudiodev(void); diff --git a/emu/port/cache.c b/emu/port/cache.c new file mode 100644 index 00000000..fff1c4e1 --- /dev/null +++ b/emu/port/cache.c @@ -0,0 +1,45 @@ +#include "dat.h" +#include "fns.h" + +/* + * no cache in hosted mode + */ +void +cinit(void) +{ +} + +void +copen(Chan *c) +{ + c->flag &= ~CCACHE; +} + +int +cread(Chan *c, uchar *b, int n, vlong off) +{ + USED(c); + USED(b); + USED(n); + USED(off); + return 0; +} + +void +cwrite(Chan *c, uchar *buf, int n, vlong off) +{ + USED(c); + USED(buf); + USED(n); + USED(off); +} + +void +cupdate(Chan *c, uchar *buf, int n, vlong off) +{ + USED(c); + USED(buf); + USED(n); + USED(off); +} + diff --git a/emu/port/chan.c b/emu/port/chan.c new file mode 100644 index 00000000..697d92c4 --- /dev/null +++ b/emu/port/chan.c @@ -0,0 +1,1404 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +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 l; + 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%lux", 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) +{ + return s == nil || s[0] == '\0'; +} + +/* + * Atomically replace *p with copy of s + */ +void +kstrdup(char **p, char *s) +{ + int n; + char *t, *prev; + + n = strlen(s)+1; + t = kmalloc(n); + if(t == nil) + panic("kstrdup: no memory"); + memmove(t, s, n); + prev = *p; + *p = t; + free(prev); +} + +static char isfrog[256]= +{ + /*NUL*/ 1, 1, 1, 1, 1, 1, 1, 1, + /*BKS*/ 1, 1, 1, 1, 1, 1, 1, 1, + /*DLE*/ 1, 1, 1, 1, 1, 1, 1, 1, + /*CAN*/ 1, 1, 1, 1, 1, 1, 1, 1 +}; + +void +chandevinit(void) +{ + int i; + + +/* isfrog[' '] = 1; */ /* let's see what happens */ + isfrog['/'] = 1; + isfrog[0x7f] = 1; + + for(i=0; devtab[i] != nil; i++) + devtab[i]->init(); +} + +Chan* +newchan(void) +{ + Chan *c; + + lock(&chanalloc.l); + c = chanalloc.free; + if(c != 0) + chanalloc.free = c->next; + unlock(&chanalloc.l); + + if(c == nil) { + c = malloc(sizeof(Chan)); + if(c == nil) + error(Enomem); + lock(&chanalloc.l); + c->fid = ++chanalloc.fid; + c->link = chanalloc.list; + chanalloc.list = c; + unlock(&chanalloc.l); + } + + /* if you get an error before associating with a dev, + close calls rootclose, a nop */ + c->type = 0; + c->flag = 0; + c->r.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; + c->mqid.path = 0; + c->mqid.vers = 0; + c->mqid.type = 0; + 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->r.ref = 1; + incref(&ncname); + return n; +} + +void +cnameclose(Cname *n) +{ + if(n == nil) + return; + if(decref(&n->r)) + 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->r.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.l); + c->next = chanalloc.free; + chanalloc.free = c; + unlock(&chanalloc.l); +} + +void +cclose(Chan *c) +{ + if(c == 0) + return; + + if(c->flag&CFREE) + panic("cclose %lux", getcallerpc(&c)); + + if(decref(&c->r)) + 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->r.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->r.ref = 1; + mh->from = from; + incref(&from->r); + +/* + 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 *new, Chan *old, int flag, char *spec) +{ + Pgrp *pg; + int order, flg; + Mhead *m, **l, *mh; + Mount *nm, *f, *um, **h; + + if(QTDIR & (old->qid.type^new->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); + + 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->env->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->env->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->r); + return nc; +} + +int +findmount(Chan **cp, Mhead **mp, int type, int dev, Qid qid) +{ + Pgrp *pg; + Mhead *m; + + pg = up->env->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->r); + if(*mp != nil) + putmhead(*mp); + *mp = m; + } + if(*cp != nil) + cclose(*cp); + incref(&m->mount->to->r); + *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->env->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->r); + 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->r); + cname = c->name; + incref(&cname->r); + 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->env->errstr, Enotdir); + if(mh != nil) + 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->env->errstr, Edoesnotexist); + }else{ + if(nerror) + *nerror = nhave+wq->nqid; + strcpy(up->env->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; +} + +/* + * 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* +memrchr(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; +} + +static void +saveregisters(void) +{ +} + +/* + * 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->env->pgrp->slash; + incref(&c->r); + 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'; + n = chartorune(&r, up->genbuf+1)+1; + if(r == 'M') + error(Enoattach); + /* + * the nodevs exceptions are + * | it only gives access to pipes you create + * e this process's environment + * s private file2chan creation space + * D private secure sockets name space + * a private TLS name space + */ + if(up->env->pgrp->nodevs && + (utfrune("|esDa", r) == nil || r == 's' && up->genbuf[n]!='\0')) + error(Enoattach); + t = devno(r, 1); + if(t == -1) + error(Ebadsharp); + c = devtab[t]->attach(up->genbuf+n); + break; + + default: + c = up->env->pgrp->dot; + incref(&c->r); + 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->env->errstr); + NameError: + len = prefix+e.off[npath]; + if(len < ERRMAX/3 || (name=memrchr(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->env->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->r); + 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\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->r); + } + + /* + * 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->r); + + 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->env->errstr; + up->env->errstr = tmperrbuf; + /* note: we depend that walk does not error */ + if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) < 0){ + up->env->errstr = createerr; + error(createerr); /* report true error */ + } + up->env->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; +} + +/* + * 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; + 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->r) == 0){ + m->mount = (Mount*)0xCafeBeef; + free(m); + } +} diff --git a/emu/port/dat.h b/emu/port/dat.h new file mode 100644 index 00000000..b245b61c --- /dev/null +++ b/emu/port/dat.h @@ -0,0 +1,512 @@ +typedef struct Block Block; +typedef struct Chan Chan; +typedef struct Cmdbuf Cmdbuf; +typedef struct Cmdtab Cmdtab; +typedef struct Cname Cname; +typedef struct Dev Dev; +typedef struct Dirtab Dirtab; +typedef struct Egrp Egrp; +typedef struct Evalue Evalue; +typedef struct Fgrp Fgrp; +typedef struct Mount Mount; +typedef struct Mntcache Mntcache; +typedef struct Mntparam Mntparam; +typedef struct Mntrpc Mntrpc; +typedef struct Mntwalk Mntwalk; +typedef struct Mnt Mnt; +typedef struct Mhead Mhead; +typedef struct Osenv Osenv; +typedef struct Pgrp Pgrp; +typedef struct Proc Proc; +typedef struct Queue Queue; +typedef struct Ref Ref; +typedef struct Rendez Rendez; +typedef struct Rept Rept; +typedef struct Rootdata Rootdata; +/*typedef struct RWlock RWlock;*/ +typedef struct RWLock RWlock; +typedef struct Procs Procs; +typedef struct Signerkey Signerkey; +typedef struct Skeyset Skeyset; +typedef struct Uqid Uqid; +typedef struct Uqidtab Uqidtab; +typedef struct Walkqid Walkqid; + +#include "lib9.h" +#undef CHDIR +#undef NAMELEN +#undef ERRLEN + +#pragma incomplete Queue +#pragma incomplete Mntrpc + +#include "fcall.h" + +#include "pool.h" + +typedef int Devgen(Chan*, char*, Dirtab*, int, int, Dir*); + +enum +{ + NERR = 32, + KNAMELEN = 28, + MAXROOT = 5*KNAMELEN, /* Maximum root pathname len of devfs-* */ + NUMSIZE = 11, + PRINTSIZE = 256, + READSTR = 1000 /* temporary buffer size for device reads */ +}; + +struct Ref +{ + Lock lk; + long ref; +}; + +struct Rendez +{ + Lock l; + Proc* p; +}; + +struct Rept +{ + Lock l; + Rendez r; + void *o; + int t; + int (*active)(void*); + int (*ck)(void*, int); + void (*f)(void*); /* called with VM acquire()'d */ +}; + +/* + * Access types in namec & channel flags + */ +enum +{ + Aaccess, /* as in access, stat */ + Abind, /* for left-hand-side of bind */ + Atodir, /* as in chdir */ + Aopen, /* for i/o */ + Amount, /* to be mounted upon */ + Acreate, /* file is to be created */ + Aremove, /* will be removed by caller */ + + COPEN = 0x0001, /* for i/o */ + CMSG = 0x0002, /* the message channel for a mount */ +/*rsc 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 */ +}; + +struct Chan +{ + Lock l; + Ref r; + 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; /* device specific data */ + Chan* mchan; /* channel to mounted server */ + Qid mqid; /* qid of root of mount point */ + Cname *name; +}; + +struct Cname +{ + Ref r; + int alen; /* allocated length */ + int len; /* strlen(s) */ + char *s; +}; + +struct Dev +{ + int dc; + char* name; + + void (*init)(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); +}; + +enum +{ + BINTR = (1<<0), + BFREE = (1<<1), + BMORE = (1<<2) /* continued in next block */ +}; + +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*); + ulong flag; +}; +#define BLEN(s) ((s)->wp - (s)->rp) +#define BALLOC(s) ((s)->lim - (s)->base) + +struct Dirtab +{ + char name[KNAMELEN]; + Qid qid; + vlong length; + long 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 r; + RWlock lock; + Chan* from; /* channel mounted upon */ + Mount* mount; /* what's mounted upon it */ + Mhead* hash; /* Hash chain */ +}; + +struct Mnt +{ + Lock l; + /* 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; /* Multiplexor id for channel check */ + Mnt* list; /* Free list */ + int flags; /* cache */ + int msize; /* data + IOHDRSZ */ + char *version; /* 9P version */ + Queue *q; /* input queue */ +}; + +enum +{ + MNTLOG = 5, + MNTHASH = 1<<MNTLOG, /* Hash to walk mount table */ + DELTAFD= 20, /* allocation quantum for process file descriptors */ + MAXNFD = 4000, /* max per process file descriptors */ + MAXKEY = 8 /* keys for signed modules */ +}; +#define MOUNTH(p,qid) ((p)->mnthash[(qid).path&((1<<MNTLOG)-1)]) + +struct Mntparam { + Chan* chan; + Chan* authchan; + char* spec; + int flags; +}; + +struct Pgrp +{ + Ref r; /* also used as a lock when mounting */ + ulong pgrpid; + RWlock ns; /* Namespace n read/one write lock */ + QLock nsh; + Mhead* mnthash[MNTHASH]; + int progmode; + int privatemem; /* deny access to /prog by debuggers */ + Chan* dot; + Chan* slash; + int nodevs; + int pin; +}; + +enum +{ + Nopin = -1 +}; + +struct Fgrp +{ + Lock l; + Ref r; + Chan** fd; + int nfd; /* number of fd slots */ + int maxfd; /* highest fd in use */ + int minfd; /* lower bound on free fd */ +}; + +struct Evalue +{ + char *var; + char *val; + int len; + Qid qid; + Evalue *next; +}; + +struct Egrp +{ + Ref r; + QLock l; + ulong path; + ulong vers; + Evalue *entries; +}; + +struct Signerkey +{ + Ref r; + char* owner; + ushort footprint; + ulong expires; + void* alg; + void* pk; + void (*pkfree)(void*); +}; + +struct Skeyset +{ + Ref r; + QLock l; + ulong flags; + char* devs; + int nkey; + Signerkey *keys[MAXKEY]; +}; + +struct Uqid +{ + Ref r; + int type; + int dev; + vlong oldpath; + vlong newpath; + Uqid* next; +}; + +enum +{ + Nqidhash = 32 +}; + +struct Uqidtab +{ + QLock l; + Uqid* qids[Nqidhash]; + ulong pathgen; +}; + +struct Osenv +{ + 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]; + Pgrp* pgrp; /* Ref to namespace, working dir and root */ + Fgrp* fgrp; /* Ref to file descriptors */ + Egrp* egrp; /* Environment vars */ + Skeyset* sigs; /* Signed module keys */ + Rendez* rend; /* Synchro point */ + Queue* waitq; /* Info about dead children */ + Queue* childq; /* Info about children for debuggers */ + void* debug; /* Debugging master */ + char* user; /* Inferno user name */ + FPU fpu; /* Floating point thread state */ + int uid; /* Numeric user id for host system */ + int gid; /* Numeric group id for host system */ + void *ui; /* User info for NT */ +}; + +enum +{ + Unknown = 0xdeadbabe, + IdleGC = 0x16, + Interp = 0x17, + BusyGC = 0x18, + Moribund +}; + +struct Proc +{ + int type; /* interpreter or not */ + char text[KNAMELEN]; + Proc* qnext; /* list of processes waiting on a Qlock */ + long pid; + Proc* next; /* list of created processes */ + Proc* prev; + Lock rlock; /* sync between sleep/swiproc for r */ + Rendez* r; /* rendezvous point slept on */ + Rendez sleep; /* place to sleep */ + int killed; /* by swiproc */ + int swipend; /* software interrupt pending for Prog */ + int syscall; /* set true under sysio for interruptable syscalls */ + int intwait; /* spin wait for note to turn up */ + int sigid; /* handle used for signal/note/exception */ + Lock sysio; /* note handler lock */ + char genbuf[128]; /* buffer used e.g. for last name element from namec */ + int nerr; /* error stack SP */ + osjmpbuf estack[NERR]; /* vector of error jump labels */ + char* kstack; + void (*func)(void*); /* saved trampoline pointer for kproc */ + void* arg; /* arg for invoked kproc function */ + void* iprog; /* work for Prog after release */ + void* prog; /* fake prog for slaves eg. exportfs */ + Osenv* env; /* effective operating system environment */ + Osenv defenv; /* default env for slaves with no prog */ + osjmpbuf privstack; /* private stack for making new kids */ + osjmpbuf sharestack; + Proc *kid; + void *kidsp; + void *os; /* host os specific data */ +}; + +#define poperror() up->nerr-- +#define waserror() (up->nerr++, ossetjmp(up->estack[up->nerr-1])) + +enum +{ + /* kproc flags */ + KPDUPPG = (1<<0), + KPDUPFDG = (1<<1), + KPDUPENVG = (1<<2), + KPX11 = (1<<8), /* needs silly amount of stack */ + KPDUP = (KPDUPPG|KPDUPFDG|KPDUPENVG) +}; + +struct Procs +{ + Lock l; + Proc* head; + Proc* tail; +}; + +struct Rootdata +{ + int dotdot; + void *ptr; + int size; + int *sizep; +}; + +extern Dev* devtab[]; +extern char *ossysname; +extern char *eve; +extern Queue* kbdq; +extern Queue* gkbdq; +extern Queue* gkscanq; +extern int Xsize; +extern int Ysize; +extern Pool* mainmem; +extern char rootdir[MAXROOT]; /* inferno root */ +extern Procs procs; +extern int sflag; +extern int xtblbit; +extern int globfs; +extern int greyscale; +extern uint qiomaxatomic; + +/* + * floating point control and status register masks + */ +enum +{ + INVAL = 0x0001, + ZDIV = 0x0002, + OVFL = 0x0004, + UNFL = 0x0008, + INEX = 0x0010, + RND_NR = 0x0000, + RND_NINF = 0x0100, + RND_PINF = 0x0200, + RND_Z = 0x0300, + RND_MASK = 0x0300 +}; + +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 + +#pragma varargck type "I" uchar* +#pragma varargck type "E" uchar* + +extern void (*mainmonitor)(int, void*, ulong); diff --git a/emu/port/dev.c b/emu/port/dev.c new file mode 100644 index 00000000..8bd1da5f --- /dev/null +++ b/emu/port/dev.c @@ -0,0 +1,448 @@ +#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, long 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 | (qid.type << 24); + db->atime = time(0); + db->mtime = kerndate; + db->length = length; + db->uid = user; + db->gid = eve; + db->muid = user; +} + +/* + * 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) +{ + USED(name); + if(tab == 0) + return -1; + if(i != DEVDOTDOT){ + /* 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 +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->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){ + (*gen)(nc, nil, tab, ntab, DEVDOTDOT, &dir); + 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->env->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("%s %s: devstat %C %llux\n", + up->text, up->env->user, + 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]; /* TO DO */ + }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->env->user. + */ +void +devpermcheck(char *fileuid, ulong perm, int omode) +{ + ulong t; + static int access[] = { 0400, 0200, 0600, 0100 }; + + if(strcmp(up->env->user, fileuid) == 0) + perm <<= 0; + else + if(strcmp(up->env->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; +} + +Block* +devbread(Chan *c, long n, ulong offset) +{ + Block *bp; + + bp = allocb(n); + 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 +devcreate(Chan *c, char *name, int mode, ulong perm) +{ + USED(c); + USED(name); + USED(mode); + USED(perm); + error(Eperm); +} + +void +devremove(Chan *c) +{ + USED(c); + error(Eperm); +} + +int +devwstat(Chan *c, uchar *dp, int n) +{ + USED(c); + USED(dp); + USED(n); + error(Eperm); + return 0; +} + +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; +} + +int +readnum(ulong off, char *buf, ulong n, ulong val, int size) +{ + char tmp[64]; + + if(size > 64) size = 64; + + snprint(tmp, sizeof(tmp), "%*.0lud ", size, val); + if(off >= size) + return 0; + if(off+n > size) + n = size-off; + memmove(buf, tmp+off, n); + return n; +} + +/* + * check that the name in a wstat is plausible + */ +void +validwstatname(char *name) +{ + validname(name, 0); + if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) + error(Efilename); +} + +Dev* +devbyname(char *name) +{ + int i; + + for(i = 0; devtab[i] != nil; i++) + if(strcmp(devtab[i]->name, name) == 0) + return devtab[i]; + return nil; +} diff --git a/emu/port/devaudio.c b/emu/port/devaudio.c new file mode 100644 index 00000000..c592f88c --- /dev/null +++ b/emu/port/devaudio.c @@ -0,0 +1,397 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "audio.h" + +Dirtab audiotab[] = +{ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "audio", {Qaudio}, 0, 0666, + "audioctl", {Qaudioctl}, 0, 0666, +}; + +static void +audioinit(void) +{ + audio_file_init(); +} + +static Chan* +audioattach(char *spec) +{ + return devattach('A', spec); +} + +static Walkqid* +audiowalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, audiotab, nelem(audiotab), devgen); +} + +static int +audiostat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, audiotab, nelem(audiotab), devgen); +} + +static Chan* +audioopen(Chan *c, int omode) +{ + c = devopen(c, omode, audiotab, nelem(audiotab), devgen); + if(waserror()){ + c->flag &= ~COPEN; + nexterror(); + } + switch(c->qid.path) { + case Qdir: + case Qaudioctl: + break; + case Qaudio: + audio_file_open(c, c->mode); + break; + default: + error(Egreg); + } + poperror(); + return c; +} + +static void +audioclose(Chan *c) +{ + if((c->flag & COPEN) == 0) + return; + + switch(c->qid.path) { + case Qdir: + case Qaudioctl: + break; + case Qaudio: + audio_file_close(c); + break; + default: + error(Egreg); + } +} + +static int ctlsummary(char*, int, Audio_t*); + +static long +audioread(Chan *c, void *va, long count, vlong offset) +{ + char *buf; + int n; + + if(c->qid.type & QTDIR) + return devdirread(c, va, count, audiotab, nelem(audiotab), devgen); + switch(c->qid.path) { + case Qaudio: + return audio_file_read(c, va, count, offset); + case Qaudioctl: + buf = smalloc(READSTR); + if(waserror()){ + free(buf); + nexterror(); + } + n = ctlsummary(buf, READSTR, getaudiodev()); + count = readstr(offset, va, n, buf); + poperror(); + free(buf); + return count; + } + return 0; +} + +static long +audiowrite(Chan *c, void *va, long count, vlong offset) +{ + switch(c->qid.path) { + case Qaudio: + return audio_file_write(c, va, count, offset); + case Qaudioctl: + return audio_ctl_write(c, va, count, offset); + } + return 0; +} + +static int sval(char*, unsigned long*, ulong, ulong); +static int str2val(svp_t*, char*, ulong*); +static char* val2str(svp_t*, ulong); + +int +audioparse(char* args, int len, Audio_t *t) +{ + int i, n; + ulong v; + Cmdbuf *cb; + ulong tf; + Audio_t info = *t; + + cb = parsecmd(args, len); + if(waserror()){ + free(cb); + return 0; + } + + tf = 0; + n = cb->nf; + for(i = 0; i < cb->nf-1; i++) { + if(strcmp(cb->f[i], "in") == 0){ + tf |= AUDIO_IN_FLAG; + continue; + } + if(strcmp(cb->f[i], "out") == 0) { + tf |= AUDIO_OUT_FLAG; + continue; + } + if(tf == 0) + tf = AUDIO_IN_FLAG | AUDIO_OUT_FLAG; + if(strcmp(cb->f[i], "bits") == 0) { + if(!str2val(audio_bits_tbl, cb->f[i+1], &v)) + break; + i++; + if(tf & AUDIO_IN_FLAG) { + info.in.flags |= AUDIO_BITS_FLAG | AUDIO_MOD_FLAG; + info.in.bits = v; + } + if(tf & AUDIO_OUT_FLAG) { + info.out.flags |= AUDIO_BITS_FLAG | AUDIO_MOD_FLAG; + info.out.bits = v; + } + } else if(strcmp(cb->f[i], "buf") == 0) { + if(!sval(cb->f[i+1], &v, Audio_Max_Val, Audio_Min_Val)) + break; + i++; + if(tf & AUDIO_IN_FLAG) { + info.in.flags |= AUDIO_BUF_FLAG | AUDIO_MOD_FLAG; + info.in.buf = v; + } + if(tf & AUDIO_OUT_FLAG) { + info.out.flags |= AUDIO_BUF_FLAG | AUDIO_MOD_FLAG; + info.out.buf = v; + } + } else if(strcmp(cb->f[i], "chans") == 0) { + if(!str2val(audio_chan_tbl, cb->f[i+1], &v)) + break; + i++; + if(tf & AUDIO_IN_FLAG) { + info.in.flags |= AUDIO_CHAN_FLAG | AUDIO_MOD_FLAG; + info.in.chan = v; + } + if(tf & AUDIO_OUT_FLAG) { + info.out.flags |= AUDIO_CHAN_FLAG | AUDIO_MOD_FLAG; + info.out.chan = v; + } + } else if(strcmp(cb->f[i], "indev") == 0) { + if(!str2val(audio_indev_tbl, cb->f[i+1], &v)) + break; + i++; + info.in.flags |= AUDIO_DEV_FLAG | AUDIO_MOD_FLAG; + info.in.dev = v; + } else if(strcmp(cb->f[i], "outdev") == 0) { + if(!str2val(audio_outdev_tbl, cb->f[i+1], &v)) + break; + i++; + info.out.flags |= AUDIO_DEV_FLAG | AUDIO_MOD_FLAG; + info.out.dev = v; + } else if(strcmp(cb->f[i], "enc") == 0) { + if(!str2val(audio_enc_tbl, cb->f[i+1], &v)) + break; + i++; + if(tf & AUDIO_IN_FLAG) { + info.in.flags |= AUDIO_ENC_FLAG | AUDIO_MOD_FLAG; + info.in.enc = v; + } + if(tf & AUDIO_OUT_FLAG) { + info.out.flags |= AUDIO_ENC_FLAG | AUDIO_MOD_FLAG; + info.out.enc = v; + } + } else if(strcmp(cb->f[i], "rate") == 0) { + if(!str2val(audio_rate_tbl, cb->f[i+1], &v)) + break; + i++; + if(tf & AUDIO_IN_FLAG) { + info.in.flags |= AUDIO_RATE_FLAG | AUDIO_MOD_FLAG; + info.in.rate = v; + } + if(tf & AUDIO_OUT_FLAG) { + info.out.flags |= AUDIO_RATE_FLAG | AUDIO_MOD_FLAG; + info.out.rate = v; + } + } else if(strcmp(cb->f[i], "vol") == 0) { + if(!sval(cb->f[i+1], &v, Audio_Max_Val, Audio_Min_Val)) + break; + i++; + if(tf & AUDIO_IN_FLAG) { + info.in.flags |= AUDIO_VOL_FLAG | AUDIO_MOD_FLAG; + info.in.left = v; + info.in.right = v; + } + if(tf & AUDIO_OUT_FLAG) { + info.out.flags |= AUDIO_VOL_FLAG | AUDIO_MOD_FLAG; + info.out.left = v; + info.out.right = v; + } + } else if(strcmp(cb->f[i], "left") == 0) { + if(!sval(cb->f[i+1], &v, Audio_Max_Val, Audio_Min_Val)) + break; + i++; + if(tf & AUDIO_IN_FLAG) { + info.in.flags |= AUDIO_LEFT_FLAG | AUDIO_MOD_FLAG; + info.in.left = v; + } + if(tf & AUDIO_OUT_FLAG) { + info.out.flags |= AUDIO_LEFT_FLAG | AUDIO_MOD_FLAG; + info.out.left = v; + } + } else if(strcmp(cb->f[i], "right") == 0) { + if(!sval(cb->f[i+1], &v, Audio_Max_Val, Audio_Min_Val)) + break; + i++; + if(tf & AUDIO_IN_FLAG) { + info.in.flags |= AUDIO_RIGHT_FLAG | AUDIO_MOD_FLAG; + info.in.right = v; + } + if(tf & AUDIO_OUT_FLAG) { + info.out.flags |= AUDIO_RIGHT_FLAG | AUDIO_MOD_FLAG; + info.out.right = v; + } + }else + break; + } + poperror(); + free(cb); + + if(i < n) + return 0; + + *t = info; /* set information back */ + return 1; +} + +static char* +audioparam(char* p, char* e, char* name, int val, svp_t* tbl) +{ + char *s; + svp_t *sv; + + if((s = val2str(tbl, val)) != nil){ + p = seprint(p, e, "%s %s", name, s); /* current setting */ + for(sv = tbl; sv->s != nil; sv++) + if(sv->v != val) + p = seprint(p, e, " %s", sv->s); /* other possible values */ + p = seprint(p, e, "\n"); + }else + p = seprint(p, e, "%s unknown\n", name); + return p; +} + +static char* +audioioparam(char* p, char* e, char* name, int ival, int oval, svp_t* tbl) +{ + if(ival == oval) + return audioparam(p, e, name, ival, tbl); + p = audioparam(seprint(p, e, "in "), e, name, ival, tbl); + p = audioparam(seprint(p, e, "out "), e, name, oval, tbl); + return p; +} + +static int +ctlsummary(char *buf, int bsize, Audio_t *adev) +{ + Audio_d *in, *out; + char *p, *e; + + in = &adev->in; + out = &adev->out; + + p = buf; + e = p + bsize; + + p = audioparam(p, e, "indev", in->dev, audio_indev_tbl); + p = audioparam(p, e, "outdev", out->dev, audio_outdev_tbl); + p = audioioparam(p, e, "enc", in->enc, out->enc, audio_enc_tbl); + p = audioioparam(p, e, "rate", in->rate, out->rate, audio_rate_tbl); + p = audioioparam(p, e, "bits", in->bits, out->bits, audio_bits_tbl); /* this one is silly */ + p = audioioparam(p, e, "chans", in->chan, out->chan, audio_chan_tbl); + /* TO DO: minimise in/out left/right where possible */ + if(in->left != in->right){ + p = seprint(p, e, "in left %d 0 100\n", in->left); + p = seprint(p, e, "in right %d 0 100\n", in->right); + }else + p = seprint(p, e, "in %d 0 100\n", in->right); + if(out->left != out->right){ + p = seprint(p, e, "out left %d 0 100\n", out->left); + p = seprint(p, e, "out right %d 0 100\n", out->right); + }else + p = seprint(p, e, "out %d 0 100\n", out->right); + p = seprint(p, e, "in buf %d %d %d\n", in->buf, Audio_Min_Val, Audio_Max_Val); + p = seprint(p, e, "out buf %d %d %d\n", out->buf, Audio_Min_Val, Audio_Max_Val); + + return p-buf; +} + +void +audio_info_init(Audio_t *t) +{ + t->in = Default_Audio_Format; + t->in.dev = Default_Audio_Input; + t->out = Default_Audio_Format; + t->out.dev = Default_Audio_Output; +} + +static int +str2val(svp_t* t, char* s, ulong *v) +{ + if(t == nil || s == nil) + return 0; + for(; t->s != nil; t++) { + if(strncmp(t->s, s, strlen(t->s)) == 0) { + *v = t->v; + return 1; + } + } + return 0; +} + +static char* +val2str(svp_t* t, ulong v) +{ + if(t == nil) + return nil; + for(; t->s != nil; t++) + if(t->v == v) + return t->s; + return nil; +} + +static int +sval(char* buf, ulong* v, ulong max, ulong min) +{ + unsigned long val = strtoul(buf, 0, 10); + + if(val > max || val < min) + return 0; + *v = val; + return 1; +} + +Dev audiodevtab = { + 'A', + "audio", + + audioinit, + audioattach, + audiowalk, + audiostat, + audioopen, + devcreate, + audioclose, + audioread, + devbread, + audiowrite, + devbwrite, + devremove, + devwstat +}; + diff --git a/emu/port/devcap.c b/emu/port/devcap.c new file mode 100644 index 00000000..00d1c2be --- /dev/null +++ b/emu/port/devcap.c @@ -0,0 +1,243 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "mp.h" +#include "libsec.h" + +/* + * Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved. + */ + +enum { + Captimeout = 15, /* seconds until expiry */ + Capidletime = 60 /* idle seconds before capwatch exits */ +}; + +typedef struct Caps Caps; +struct Caps +{ + uchar hash[SHA1dlen]; + ulong time; + Caps* next; +}; + +struct { + QLock l; + Caps* caps; + int kpstarted; +} allcaps; + +enum { + Qdir, + Qhash, + Quse +}; + +static Dirtab capdir[] = +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "capuse", {Quse, 0}, 0, 0222, + "caphash", {Qhash, 0}, 0, 0200, +}; + +static int ncapdir = nelem(capdir); + +static void +capwatch(void *a) +{ + Caps *c, **l; + int idletime; + + USED(a); + idletime = 0; + for(;;){ + osmillisleep(30*1000); + qlock(&allcaps.l); + for(l = &allcaps.caps; (c = *l) != nil;) + if(++c->time > Captimeout){ + *l = c->next; + free(c); + }else + l = &c->next; + if(allcaps.caps == nil){ + if(++idletime > Capidletime){ + allcaps.kpstarted = 0; + qunlock(&allcaps.l); + pexit("", 0); + } + }else + idletime = 0; + qunlock(&allcaps.l); + } +} + +static Chan * +capattach(char *spec) +{ + return devattach(0x00A4, spec); /* L'¤' */ +} + +static Walkqid* +capwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, capdir, nelem(capdir), devgen); +} + +static int +capstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, capdir, nelem(capdir), devgen); +} + +static Chan* +capopen(Chan *c, int omode) +{ + if(c->qid.type & QTDIR) { + if(omode != OREAD) + error(Eisdir); + c->mode = omode; + c->flag |= COPEN; + c->offset = 0; + return c; + } + + if(c->qid.path == Qhash && !iseve()) + error(Eperm); + + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +capclose(Chan *c) +{ + USED(c); +} + +static long +capread(Chan *c, void *va, long n, vlong vl) +{ + USED(vl); + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, va, n, capdir, ncapdir, devgen); + + default: + error(Eperm); + break; + } + return n; +} + +static int +capwritehash(uchar *a, int l) +{ + Caps *c; + + if(l != SHA1dlen) + return -1; + c = malloc(sizeof(*c)); + if(c == nil) + return -1; + memmove(c->hash, a, l); + c->time = 0; + qlock(&allcaps.l); + c->next = allcaps.caps; + allcaps.caps = c; + if(!allcaps.kpstarted){ + allcaps.kpstarted = 1; + kproc("capwatch", capwatch, 0, 0); + } + qunlock(&allcaps.l); + return 0; +} + +static int +capwriteuse(uchar *a, int len) +{ + int n; + uchar digest[SHA1dlen]; + char buf[128], *p, *users[3]; + Caps *c, **l; + + if(len >= sizeof(buf)-1) + return -1; + memmove(buf, a, len); + buf[len] = 0; + p = strrchr(buf, '@'); + if(p == nil) + return -1; + *p++ = 0; + len = strlen(p); + n = strlen(buf); + if(len == 0 || n == 0) + return -1; + hmac_sha1((uchar*)buf, n, (uchar*)p, len, digest, nil); + n = getfields(buf, users, nelem(users), 0, "@"); + if(n == 1) + users[1] = users[0]; + else if(n != 2) + return -1; + if(*users[0] == 0 || *users[1] == 0) + return -1; + qlock(&allcaps.l); + for(l = &allcaps.caps; (c = *l) != nil; l = &c->next) + if(memcmp(c->hash, digest, sizeof(c->hash)) == 0){ + *l = c->next; + qunlock(&allcaps.l); + free(c); + if(n == 2 && strcmp(up->env->user, users[0]) != 0) + return -1; + setid(users[1], 0); /* could use users[2] to mean `host OS user' */ + return 0; + } + qunlock(&allcaps.l); + return -1; +} + +static long +capwrite(Chan* c, void* buf, long n, vlong offset) +{ + USED(offset); + switch((ulong)c->qid.path){ + case Qhash: + if(capwritehash(buf, n) < 0) + error(Ebadarg); + return n; + case Quse: + if(capwriteuse(buf, n) < 0) + error("invalid capability"); + return n; + } + error(Ebadarg); + return 0; +} + +static void +capremove(Chan *c) +{ + if(c->qid.path != Qhash || !iseve()) + error(Eperm); + ncapdir = nelem(capdir)-1; +} + +Dev capdevtab = { + 0x00A4, /* L'¤' */ + "cap", + + devinit, + capattach, + capwalk, + capstat, + capopen, + devcreate, + capclose, + capread, + devbread, + capwrite, + devbwrite, + capremove, + devwstat +}; diff --git a/emu/port/devcmd.c b/emu/port/devcmd.c new file mode 100644 index 00000000..4822c49a --- /dev/null +++ b/emu/port/devcmd.c @@ -0,0 +1,661 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +enum +{ + Qtopdir, /* top level directory */ + Qcmd, + Qclonus, + Qconvdir, + Qconvbase, + Qdata = Qconvbase, + Qctl, + Qstatus, + Qwait, + + Debug=0 /* to help debug os.c */ +}; +#define TYPE(x) ((ulong)(x).path & 0xf) +#define CONV(x) (((ulong)(x).path >> 4)&0xfff) +#define QID(c, y) (((c)<<4) | (y)) + +typedef struct Conv Conv; +struct Conv +{ + int x; + int inuse; + int wcount; + int rcount; + int rfd; + int wfd; + int perm; + char* owner; + char* state; + Cmdbuf* cmd; + char* dir; + QLock l; /* protects state changes */ + Queue* waitq; + void* child; + char* error; /* on start up */ + int nice; + short killonclose; + short killed; + Rendez startr; +}; + +static struct +{ + QLock l; + int nc; + int maxconv; + Conv** conv; +} cmd; + +static Conv* cmdclone(char*); +static void cmdproc(void*); + +static int +cmd3gen(Chan *c, int i, Dir *dp) +{ + Qid q; + Conv *cv; + + cv = cmd.conv[CONV(c->qid)]; + switch(i){ + default: + return -1; + case Qdata: + mkqid(&q, QID(CONV(c->qid), Qdata), 0, QTFILE); + devdir(c, q, "data", 0, cv->owner, cv->perm, dp); + return 1; + case Qctl: + mkqid(&q, QID(CONV(c->qid), Qctl), 0, QTFILE); + devdir(c, q, "ctl", 0, cv->owner, cv->perm, dp); + return 1; + case Qstatus: + mkqid(&q, QID(CONV(c->qid), Qstatus), 0, QTFILE); + devdir(c, q, "status", 0, cv->owner, 0444, dp); + return 1; + case Qwait: + mkqid(&q, QID(CONV(c->qid), Qwait), 0, QTFILE); + devdir(c, q, "wait", 0, cv->owner, 0444, dp); + return 1; + } +} + +static int +cmdgen(Chan *c, char *name, Dirtab *d, int nd, int s, Dir *dp) +{ + Qid q; + Conv *cv; + + USED(name); + USED(nd); + USED(d); + + if(s == DEVDOTDOT){ + switch(TYPE(c->qid)){ + case Qtopdir: + case Qcmd: + mkqid(&q, QID(0, Qtopdir), 0, QTDIR); + devdir(c, q, "#C", 0, eve, DMDIR|0555, dp); + break; + case Qconvdir: + mkqid(&q, QID(0, Qcmd), 0, QTDIR); + devdir(c, q, "cmd", 0, eve, DMDIR|0555, dp); + break; + default: + panic("cmdgen %llux", c->qid.path); + } + return 1; + } + + switch(TYPE(c->qid)) { + case Qtopdir: + if(s >= 1) + return -1; + mkqid(&q, QID(0, Qcmd), 0, QTDIR); + devdir(c, q, "cmd", 0, "cmd", DMDIR|0555, dp); + return 1; + case Qcmd: + if(s < cmd.nc) { + cv = cmd.conv[s]; + mkqid(&q, QID(s, Qconvdir), 0, QTDIR); + sprint(up->genbuf, "%d", s); + devdir(c, q, up->genbuf, 0, cv->owner, DMDIR|0555, dp); + return 1; + } + s -= cmd.nc; + if(s == 0){ + mkqid(&q, QID(0, Qclonus), 0, QTFILE); + devdir(c, q, "clone", 0, "cmd", 0666, dp); + return 1; + } + return -1; + case Qclonus: + if(s == 0){ + mkqid(&q, QID(0, Qclonus), 0, QTFILE); + devdir(c, q, "clone", 0, "cmd", 0666, dp); + return 1; + } + return -1; + case Qconvdir: + return cmd3gen(c, Qconvbase+s, dp); + case Qdata: + case Qctl: + case Qstatus: + case Qwait: + return cmd3gen(c, TYPE(c->qid), dp); + } + return -1; +} + +static void +cmdinit(void) +{ + cmd.maxconv = 100; + cmd.conv = mallocz(sizeof(Conv*)*(cmd.maxconv+1), 1); + /* cmd.conv is checked by cmdattach, below */ +} + +static Chan * +cmdattach(char *spec) +{ + Chan *c; + + if(cmd.conv == nil) + error(Enomem); + c = devattach('C', spec); + mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR); + return c; +} + +static Walkqid* +cmdwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, cmdgen); +} + +static int +cmdstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, cmdgen); +} + +static Chan * +cmdopen(Chan *c, int omode) +{ + int perm; + Conv *cv; + char *user; + + perm = 0; + omode = openmode(omode); + 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 Qcmd: + case Qconvdir: + case Qstatus: + if(omode != OREAD) + error(Eperm); + break; + case Qclonus: + qlock(&cmd.l); + if(waserror()){ + qunlock(&cmd.l); + nexterror(); + } + cv = cmdclone(up->env->user); + poperror(); + qunlock(&cmd.l); + if(cv == 0) + error(Enodev); + mkqid(&c->qid, QID(cv->x, Qctl), 0, QTFILE); + break; + case Qdata: + case Qctl: + case Qwait: + qlock(&cmd.l); + cv = cmd.conv[CONV(c->qid)]; + qlock(&cv->l); + if(waserror()){ + qunlock(&cv->l); + qunlock(&cmd.l); + nexterror(); + } + user = up->env->user; + if((perm & (cv->perm>>6)) != perm) { + if(strcmp(user, cv->owner) != 0 || + (perm & cv->perm) != perm) + error(Eperm); + } + switch(TYPE(c->qid)){ + case Qdata: + if(omode == OWRITE || omode == ORDWR) + cv->wcount++; + if(omode == OREAD || omode == ORDWR) + cv->rcount++; + c->mode = omode; + break; + case Qwait: + if(cv->waitq == nil) + cv->waitq = qopen(1024, Qmsg, nil, 0); + break; + } + cv->inuse++; + if(cv->inuse == 1) { + cv->state = "Open"; + kstrdup(&cv->owner, user); + cv->perm = 0660; + cv->nice = 0; + } + poperror(); + qunlock(&cv->l); + qunlock(&cmd.l); + break; + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +closeconv(Conv *c) +{ + kstrdup(&c->owner, "cmd"); + kstrdup(&c->dir, rootdir); + c->perm = 0666; + c->state = "Closed"; + c->killonclose = 0; + c->killed = 0; + c->nice = 0; + free(c->cmd); + c->cmd = nil; + if(c->waitq != nil){ + qfree(c->waitq); + c->waitq = nil; + } + free(c->error); + c->error = nil; +} + +static void +cmdclose(Chan *c) +{ + Conv *cc; + int r; + + if((c->flag & COPEN) == 0) + return; + + switch(TYPE(c->qid)) { + case Qctl: + case Qdata: + case Qwait: + cc = cmd.conv[CONV(c->qid)]; + qlock(&cc->l); + if(TYPE(c->qid) == Qdata){ + if(c->mode == OWRITE || c->mode == ORDWR){ + if(--cc->wcount == 0 && cc->wfd != -1){ + close(cc->wfd); + cc->wfd = -1; + } + } + if(c->mode == OREAD || c->mode == ORDWR){ + if(--cc->rcount == 0 && cc->rfd != -1){ + close(cc->rfd); + cc->rfd = -1; + } + } + } + + r = --cc->inuse; + if(cc->child != nil){ + if(!cc->killed) + if(r == 0 || (cc->killonclose && TYPE(c->qid) == Qctl)){ + oscmdkill(cc->child); + cc->killed = 1; + } + }else if(r == 0) + closeconv(cc); + + qunlock(&cc->l); + break; + } +} + +static long +cmdread(Chan *ch, void *a, long n, vlong offset) +{ + Conv *c; + char *p, *cmds; + + USED(offset); + + p = a; + switch(TYPE(ch->qid)) { + default: + error(Eperm); + case Qcmd: + case Qtopdir: + case Qconvdir: + return devdirread(ch, a, n, 0, 0, cmdgen); + case Qctl: + sprint(up->genbuf, "%ld", CONV(ch->qid)); + return readstr(offset, p, n, up->genbuf); + case Qstatus: + c = cmd.conv[CONV(ch->qid)]; + cmds = ""; + if(c->cmd != nil) + cmds = c->cmd->f[1]; + snprint(up->genbuf, sizeof(up->genbuf), "cmd/%d %d %s %q %q\n", + c->x, c->inuse, c->state, c->dir, cmds); + return readstr(offset, p, n, up->genbuf); + case Qdata: + c = cmd.conv[CONV(ch->qid)]; + qlock(&c->l); + if(c->rfd == -1){ + qunlock(&c->l); + return 0; + } + qunlock(&c->l); + osenter(); + n = read(c->rfd, a, n); + osleave(); + if(n < 0) + oserror(); + return n; + case Qwait: + c = cmd.conv[CONV(ch->qid)]; + return qread(c->waitq, a, n); + } +} + +static int +cmdstarted(void *a) +{ + Conv *c; + + c = a; + return c->child != nil || c->error != nil; +} + +enum +{ + CMdir, + CMexec, + CMkill, + CMnice, + CMkillonclose +}; + +static +Cmdtab cmdtab[] = { + CMdir, "dir", 2, + CMexec, "exec", 0, + CMkill, "kill", 1, + CMnice, "nice", 0, + CMkillonclose, "killonclose", 0, +}; + +static long +cmdwrite(Chan *ch, void *a, long n, vlong offset) +{ + int r; + Conv *c; + Cmdbuf *cb; + Cmdtab *ct; + + USED(offset); + + switch(TYPE(ch->qid)) { + default: + error(Eperm); + case Qctl: + c = cmd.conv[CONV(ch->qid)]; + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + ct = lookupcmd(cb, cmdtab, nelem(cmdtab)); + switch(ct->index){ + case CMdir: + kstrdup(&c->dir, cb->f[1]); + break; + case CMexec: + poperror(); /* cb */ + qlock(&c->l); + if(waserror()){ + qunlock(&c->l); + free(cb); + nexterror(); + } + if(c->child != nil || c->cmd != nil || c->wfd != -1 || c->rfd != -1) + error(Einuse); + if(cb->nf < 1) + error(Etoosmall); + kproc("cmdproc", cmdproc, c, 0); /* cmdproc held back until unlock below */ + free(c->cmd); + c->cmd = cb; /* don't free cb */ + c->state = "Execute"; + poperror(); + qunlock(&c->l); + while(waserror()) + ; + Sleep(&c->startr, cmdstarted, c); + poperror(); + if(c->error) + error(c->error); + return n; /* avoid free(cb) below */ + case CMkill: + qlock(&c->l); + if(waserror()){ + qunlock(&c->l); + nexterror(); + } + if(c->child == nil) + error("not started"); + if(oscmdkill(c->child) < 0) + oserror(); + poperror(); + qunlock(&c->l); + break; + case CMnice: + c->nice = cb->nf > 1? atoi(cb->f[1]): 1; + break; + case CMkillonclose: + c->killonclose = 1; + break; + } + poperror(); + free(cb); + break; + case Qdata: + c = cmd.conv[CONV(ch->qid)]; + qlock(&c->l); + if(c->wfd == -1){ + qunlock(&c->l); + error(Ehungup); + } + qunlock(&c->l); + osenter(); + r = write(c->wfd, a, n); + osleave(); + if(r == 0) + error(Ehungup); + if(r < 0) { + /* XXX perhaps should kill writer "write on closed pipe" here, 2nd time around? */ + oserror(); + } + return r; + } + return n; +} + +static int +cmdwstat(Chan *c, uchar *dp, int n) +{ + Dir *d; + Conv *cv; + + switch(TYPE(c->qid)){ + default: + error(Eperm); + case Qctl: + case Qdata: + d = malloc(sizeof(*d)+n); + if(d == nil) + error(Enomem); + if(waserror()){ + free(d); + nexterror(); + } + n = convM2D(dp, n, d, (char*)&d[1]); + if(n == 0) + error(Eshortstat); + cv = cmd.conv[CONV(c->qid)]; + if(!iseve() && strcmp(up->env->user, cv->owner) != 0) + error(Eperm); + if(!emptystr(d->uid)) + kstrdup(&cv->owner, d->uid); + if(d->mode != ~0UL) + cv->perm = d->mode & 0777; + poperror(); + free(d); + break; + } + return n; +} + +static Conv* +cmdclone(char *user) +{ + Conv *c, **pp, **ep; + + c = nil; + ep = &cmd.conv[cmd.maxconv]; + for(pp = cmd.conv; pp < ep; pp++) { + c = *pp; + if(c == nil) { + c = malloc(sizeof(Conv)); + if(c == nil) + error(Enomem); + qlock(&c->l); + c->inuse = 1; + c->x = pp - cmd.conv; + cmd.nc++; + *pp = c; + break; + } + if(canqlock(&c->l)){ + if(c->inuse == 0 && c->child == nil) + break; + qunlock(&c->l); + } + } + if(pp >= ep) + return nil; + + c->inuse = 1; + kstrdup(&c->owner, user); + kstrdup(&c->dir, rootdir); + c->perm = 0660; + c->state = "Closed"; + c->rfd = -1; + c->wfd = -1; + + qunlock(&c->l); + return c; +} + +static void +cmdproc(void *a) +{ + Conv *c; + int n; + char status[ERRMAX]; + void *t; + + c = a; + qlock(&c->l); + if(Debug) + print("f[0]=%q f[1]=%q\n", c->cmd->f[0], c->cmd->f[1]); + if(waserror()){ + if(Debug) + print("failed: %q\n", up->env->errstr); + kstrdup(&c->error, up->env->errstr); + c->state = "Done"; + qunlock(&c->l); + Wakeup(&c->startr); + pexit("cmdproc", 0); + } + t = oscmd(c->cmd->f+1, c->nice, c->dir, &c->rfd, &c->wfd); + if(t == nil) + oserror(); + c->child = t; /* to allow oscmdkill */ + poperror(); + qunlock(&c->l); + Wakeup(&c->startr); + if(Debug) + print("started\n"); + while(waserror()) + oscmdkill(t); + osenter(); + n = oscmdwait(t, status, sizeof(status)); + osleave(); + if(n < 0){ + oserrstr(up->genbuf, sizeof(up->genbuf)); + n = snprint(status, sizeof(status), "0 0 0 0 %q", up->genbuf); + } + qlock(&c->l); + c->child = nil; + oscmdfree(t); + if(Debug){ + status[n]=0; + print("done %d %d: %q\n", c->rfd, c->wfd, status); + } + if(c->inuse > 0){ + c->state = "Done"; + if(c->waitq != nil) + qproduce(c->waitq, status, n); + }else + closeconv(c); + qunlock(&c->l); + pexit("", 0); +} + +Dev cmddevtab = { + 'C', + "cmd", + + cmdinit, + cmdattach, + cmdwalk, + cmdstat, + cmdopen, + devcreate, + cmdclose, + cmdread, + devbread, + cmdwrite, + devbwrite, + devremove, + cmdwstat +}; diff --git a/emu/port/devcons.c b/emu/port/devcons.c new file mode 100644 index 00000000..efe49838 --- /dev/null +++ b/emu/port/devcons.c @@ -0,0 +1,611 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "version.h" +#include "mp.h" +#include "libsec.h" +#include "keyboard.h" + +extern int cflag; +int exdebug; +extern int keepbroken; + +enum +{ + Qdir, + Qcons, + Qconsctl, + Qdrivers, + Qhostowner, + Qhoststdin, + Qhoststdout, + Qhoststderr, + Qjit, + Qkeyboard, + Qkprint, + Qmemory, + Qmsec, + Qnotquiterandom, + Qnull, + Qpin, + Qrandom, + Qscancode, + Qsysctl, + Qsysname, + Qtime, + Quser +}; + +Dirtab contab[] = +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "cons", {Qcons}, 0, 0666, + "consctl", {Qconsctl}, 0, 0222, + "drivers", {Qdrivers}, 0, 0444, + "hostowner", {Qhostowner}, 0, 0644, + "hoststdin", {Qhoststdin}, 0, 0444, + "hoststdout", {Qhoststdout}, 0, 0222, + "hoststderr", {Qhoststderr}, 0, 0222, + "jit", {Qjit}, 0, 0666, + "keyboard", {Qkeyboard}, 0, 0666, + "kprint", {Qkprint}, 0, 0444, + "memory", {Qmemory}, 0, 0444, + "msec", {Qmsec}, NUMSIZE, 0444, + "notquiterandom", {Qnotquiterandom}, 0, 0444, + "null", {Qnull}, 0, 0666, + "pin", {Qpin}, 0, 0666, + "random", {Qrandom}, 0, 0444, + "scancode", {Qscancode}, 0, 0444, + "sysctl", {Qsysctl}, 0, 0644, + "sysname", {Qsysname}, 0, 0644, + "time", {Qtime}, 0, 0644, + "user", {Quser}, 0, 0644, +}; + +Queue* gkscanq; /* Graphics keyboard raw scancodes */ +char* gkscanid; /* name of raw scan format (if defined) */ +Queue* gkbdq; /* Graphics keyboard unprocessed input */ +Queue* kbdq; /* Console window unprocessed keyboard input */ +Queue* lineq; /* processed console input */ + +char *ossysname; + +static struct +{ + RWlock l; + Queue* q; +} kprintq; + +vlong timeoffset; + +extern int dflag; + +static int sysconwrite(void*, ulong); +extern char** rebootargv; + +static struct +{ + QLock q; + QLock gq; /* separate lock for the graphical input */ + + int raw; /* true if we shouldn't process input */ + Ref ctl; /* number of opens to the control file */ + Ref ptr; /* number of opens to the ptr file */ + int scan; /* true if reading raw scancodes */ + int x; /* index into line */ + char line[1024]; /* current input line */ + + Rune c; + int count; +} kbd; + +void +kbdslave(void *a) +{ + char b; + + USED(a); + for(;;) { + b = readkbd(); + if(kbd.raw == 0) + write(1, &b, 1); + qproduce(kbdq, &b, 1); + } + pexit("kbdslave", 0); +} + +void +gkbdputc(Queue *q, int ch) +{ + int n; + Rune r; + static uchar kc[5*UTFmax]; + static int nk, collecting = 0; + char buf[UTFmax]; + + r = ch; + if(r == Latin) { + collecting = 1; + nk = 0; + return; + } + if(collecting) { + int c; + nk += runetochar((char*)&kc[nk], &r); + c = latin1(kc, nk); + if(c < -1) /* need more keystrokes */ + return; + collecting = 0; + if(c == -1) { /* invalid sequence */ + qproduce(q, kc, nk); + return; + } + r = (Rune)c; + } + n = runetochar(buf, &r); + if(n == 0) + return; + /* if(!isdbgkey(r)) */ + qproduce(q, buf, n); +} + +void +consinit(void) +{ + kbdq = qopen(512, 0, 0, 0); + if(kbdq == 0) + panic("no memory"); + lineq = qopen(512, 0, 0, 0); + if(lineq == 0) + panic("no memory"); + gkbdq = qopen(512, 0, 0, 0); + if(gkbdq == 0) + panic("no memory"); + randominit(); +} + +/* + * return true if current user is eve + */ +int +iseve(void) +{ + return strcmp(eve, up->env->user) == 0; +} + +static Chan* +consattach(char *spec) +{ + static int kp; + + if (kp == 0 && !dflag) { + kproc("kbd", kbdslave, 0, 0); + kp = 1; + } + return devattach('c', spec); +} + +static Walkqid* +conswalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, contab, nelem(contab), devgen); +} + +static int +consstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, contab, nelem(contab), devgen); +} + +static Chan* +consopen(Chan *c, int omode) +{ + c = devopen(c, omode, contab, nelem(contab), devgen); + switch((ulong)c->qid.path) { + case Qconsctl: + incref(&kbd.ctl); + break; + case Qscancode: + qlock(&kbd.gq); + if(gkscanq || !gkscanid) { + qunlock(&kbd.q); + c->flag &= ~COPEN; + if(gkscanq) + error(Einuse); + else + error(Ebadarg); + } + gkscanq = qopen(256, 0, nil, nil); + qunlock(&kbd.gq); + break; + case Qkprint: + wlock(&kprintq.l); + if(kprintq.q != nil){ + wunlock(&kprintq.l); + c->flag &= ~COPEN; + error(Einuse); + } + kprintq.q = qopen(32*1024, 0, 0, 0); + if(kprintq.q == nil){ + wunlock(&kprintq.l); + c->flag &= ~COPEN; + error(Enomem); + } + qnoblock(kprintq.q, 1); + wunlock(&kprintq.l); + break; + } + return c; +} + +static void +consclose(Chan *c) +{ + if((c->flag & COPEN) == 0) + return; + + switch((ulong)c->qid.path) { + case Qconsctl: + if(decref(&kbd.ctl) == 0) + kbd.raw = 0; + break; + case Qscancode: + qlock(&kbd.gq); + if(gkscanq) { + qfree(gkscanq); + gkscanq = 0; + } + qunlock(&kbd.gq); + break; + case Qkprint: + wlock(&kprintq.l); + qfree(kprintq.q); + kprintq.q = nil; + wunlock(&kprintq.l); + break; + } +} + +static long +consread(Chan *c, void *va, long count, vlong offset) +{ + int i, n, ch, eol; + char *p, buf[64]; + + if(c->qid.type & QTDIR) + return devdirread(c, va, count, contab, nelem(contab), devgen); + + switch((ulong)c->qid.path) { + default: + error(Egreg); + case Qsysctl: + return readstr(offset, va, count, VERSION); + case Qsysname: + if(ossysname == nil) + return 0; + return readstr(offset, va, count, ossysname); + case Qrandom: + return randomread(va, count); + case Qnotquiterandom: + genrandom(va, count); + return count; + case Qpin: + p = "pin set"; + if(up->env->pgrp->pin == Nopin) + p = "no pin"; + return readstr(offset, va, count, p); + case Qhostowner: + return readstr(offset, va, count, eve); + case Qhoststdin: + return read(0, va, count); /* should be pread */ + case Quser: + return readstr(offset, va, count, up->env->user); + case Qjit: + snprint(buf, sizeof(buf), "%d", cflag); + return readstr(offset, va, count, buf); + case Qtime: + snprint(buf, sizeof(buf), "%.lld", timeoffset + osusectime()); + return readstr(offset, va, count, buf); + case Qdrivers: + p = malloc(READSTR); + if(p == nil) + error(Enomem); + n = 0; + for(i = 0; devtab[i] != nil; i++) + n += snprint(p+n, READSTR-n, "#%C %s\n", devtab[i]->dc, devtab[i]->name); + n = readstr(offset, va, count, p); + free(p); + return n; + case Qmemory: + return poolread(va, count, offset); + + case Qnull: + return 0; + case Qmsec: + return readnum(offset, va, count, osmillisec(), NUMSIZE); + case Qcons: + qlock(&kbd.q); + if(waserror()){ + qunlock(&kbd.q); + nexterror(); + } + + if(dflag) + error(Enonexist); + + while(!qcanread(lineq)) { + qread(kbdq, &kbd.line[kbd.x], 1); + ch = kbd.line[kbd.x]; + if(kbd.raw){ + qiwrite(lineq, &kbd.line[kbd.x], 1); + continue; + } + 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, va, count); + qunlock(&kbd.q); + poperror(); + return n; + case Qscancode: + if(offset == 0) + return readstr(0, va, count, gkscanid); + else + return qread(gkscanq, va, count); + case Qkeyboard: + return qread(gkbdq, va, count); + case Qkprint: + rlock(&kprintq.l); + if(waserror()){ + runlock(&kprintq.l); + nexterror(); + } + n = qread(kprintq.q, va, count); + poperror(); + runlock(&kprintq.l); + return n; + } +} + +static long +conswrite(Chan *c, void *va, long count, vlong offset) +{ + char buf[128]; + int x; + + USED(offset); + + if(c->qid.type & QTDIR) + error(Eperm); + + switch((ulong)c->qid.path) { + default: + error(Egreg); + case Qcons: + if(canrlock(&kprintq.l)){ + if(kprintq.q != nil){ + if(waserror()){ + runlock(&kprintq.l); + nexterror(); + } + qwrite(kprintq.q, va, count); + poperror(); + runlock(&kprintq.l); + return count; + } + runlock(&kprintq.l); + } + return write(1, va, count); + case Qsysctl: + return sysconwrite(va, count); + case Qconsctl: + if(count >= sizeof(buf)) + count = sizeof(buf)-1; + strncpy(buf, va, count); + buf[count] = 0; + if(strncmp(buf, "rawon", 5) == 0) { + kbd.raw = 1; + return count; + } + else + if(strncmp(buf, "rawoff", 6) == 0) { + kbd.raw = 0; + return count; + } + error(Ebadctl); + case Qkeyboard: + for(x=0; x<count; ) { + Rune r; + x += chartorune(&r, &((char*)va)[x]); + gkbdputc(gkbdq, r); + } + return count; + case Qnull: + return count; + case Qpin: + if(up->env->pgrp->pin != Nopin) + error("pin already set"); + if(count >= sizeof(buf)) + count = sizeof(buf)-1; + strncpy(buf, va, count); + buf[count] = '\0'; + up->env->pgrp->pin = atoi(buf); + return count; + case Qtime: + if(count >= sizeof(buf)) + count = sizeof(buf)-1; + strncpy(buf, va, count); + buf[count] = '\0'; + timeoffset = strtoll(buf, 0, 0)-osusectime(); + return count; + case Quser: + if(count >= sizeof(buf)) + error(Ebadarg); + strncpy(buf, va, count); + buf[count] = '\0'; + if(count > 0 && buf[count-1] == '\n') + buf[--count] = '\0'; + if(count == 0) + error(Ebadarg); + if(strcmp(up->env->user, eve) != 0) + error(Eperm); + setid(buf, 0); + return count; + case Qhostowner: + if(count >= sizeof(buf)) + error(Ebadarg); + strncpy(buf, va, count); + buf[count] = '\0'; + if(count > 0 && buf[count-1] == '\n') + buf[--count] = '\0'; + if(count == 0) + error(Ebadarg); + if(strcmp(up->env->user, eve) != 0) + error(Eperm); + kstrdup(&eve, buf); + return count; + case Qhoststdout: + return write(1, va, count); + case Qhoststderr: + return write(2, va, count); + case Qjit: + if(count >= sizeof(buf)) + count = sizeof(buf)-1; + strncpy(buf, va, count); + buf[count] = '\0'; + x = atoi(buf); + if (x < 0 || x > 9) + error(Ebadarg); + cflag = x; + return count; + case Qsysname: + if(count >= sizeof(buf)) + count = sizeof(buf)-1; + strncpy(buf, va, count); + buf[count] = '\0'; + kstrdup(&ossysname, buf); + return count; + } + return 0; +} + +static int +sysconwrite(void *va, ulong count) +{ + Cmdbuf *cb; + int e; + cb = parsecmd(va, count); + if(waserror()){ + free(cb); + nexterror(); + } + if(cb->nf == 0) + error(Enoctl); + if(strcmp(cb->f[0], "reboot") == 0){ + osreboot(rebootargv[0], rebootargv); + error("reboot not supported"); + }else if(strcmp(cb->f[0], "halt") == 0){ + if(cb->nf > 1) + e = atoi(cb->f[1]); + else + e = 0; + cleanexit(e); /* XXX ignored for the time being (and should be a string anyway) */ + }else if(strcmp(cb->f[0], "broken") == 0) + keepbroken = 1; + else if(strcmp(cb->f[0], "nobroken") == 0) + keepbroken = 0; + else if(strcmp(cb->f[0], "exdebug") == 0) + exdebug = !exdebug; + else + error(Enoctl); + poperror(); + free(cb); + return count; +} + +Dev consdevtab = { + 'c', + "cons", + + consinit, + consattach, + conswalk, + consstat, + consopen, + devcreate, + consclose, + consread, + devbread, + conswrite, + devbwrite, + devremove, + devwstat +}; + +static ulong randn; + +static void +seedrand(void) +{ + randomread((void*)&randn, sizeof(randn)); +} + +int +nrand(int n) +{ + if(randn == 0) + seedrand(); + randn = randn*1103515245 + 12345 + osusectime(); + return (randn>>16) % n; +} + +int +rand(void) +{ + nrand(1); + return randn; +} + +ulong +truerand(void) +{ + ulong x; + + randomread(&x, sizeof(x)); + return x; +} + +QLock grandomlk; + +void +_genrandomqlock(void) +{ + qlock(&grandomlk); +} + + +void +_genrandomqunlock(void) +{ + qunlock(&grandomlk); +} diff --git a/emu/port/devconsx.c b/emu/port/devconsx.c new file mode 100644 index 00000000..089888bc --- /dev/null +++ b/emu/port/devconsx.c @@ -0,0 +1,833 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "version.h" +#include "libcrypt.h" +#include "keyboard.h" + +extern int cflag; +extern int keepbroken; + +enum +{ + Qdir, + Qcons, + Qsysctl, + Qconsctl, + Qdrivers, + Qkeyboard, + Qkprint, + Qscancode, + Qmemory, + Qmsec, + Qnull, + Qpin, + Qpointer, + Qrandom, + Qnotquiterandom, + Qsysname, + Qtime, + Quser, + Qjit, + Qcaphash, + Qcapuse +}; + +Dirtab contab[] = +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "cons", {Qcons}, 0, 0666, + "consctl", {Qconsctl}, 0, 0222, + "sysctl", {Qsysctl}, 0, 0644, + "keyboard", {Qkeyboard}, 0, 0666, + "kprint", {Qkprint}, 0, 0444, + "scancode", {Qscancode}, 0, 0444, + "memory", {Qmemory}, 0, 0444, + "msec", {Qmsec}, NUMSIZE, 0444, + "notquiterandom", {Qnotquiterandom}, 0, 0444, + "null", {Qnull}, 0, 0666, + "pin", {Qpin}, 0, 0666, + "pointer", {Qpointer}, 0, 0666, + "random", {Qrandom}, 0, 0444, + "sysname", {Qsysname}, 0, 0644, + "user", {Quser}, 0, 0644, + "time", {Qtime}, 0, 0644, + "drivers", {Qdrivers}, 0, 0444, + "jit", {Qjit}, 0, 0666, + "caphash", {Qcaphash}, 0, 0200, + "capuse", {Qcapuse}, 0, 0222, +}; + +enum +{ + MBS = 1024 +}; + +Queue* gkscanq; /* Graphics keyboard raw scancodes */ +char* gkscanid; /* name of raw scan format (if defined) */ +Queue* gkbdq; /* Graphics keyboard unprocessed input */ +Queue* kbdq; /* Console window unprocessed keyboard input */ +Queue* lineq; /* processed console input */ + +static Ref capopen; + +char *ossysname; + +static struct +{ + RWlock l; + Queue* q; +} kprintq; + +vlong timeoffset; + +static ulong randomread(void *xp, ulong n); +static void randominit(void); + +extern int dflag; + +static int sysconwrite(void*, ulong); +static int argsread(ulong, void*, ulong); +extern int rebootargc; +extern char** rebootargv; + +static struct +{ + QLock q; + QLock gq; /* separate lock for the graphical input */ + + int raw; /* true if we shouldn't process input */ + Ref ctl; /* number of opens to the control file */ + int kbdr; /* number of open reads to the keyboard */ + Ref ptr; /* number of opens to the ptr file */ + int scan; /* true if reading raw scancodes */ + int x; /* index into line */ + char line[1024]; /* current input line */ + + Rune c; + int count; +} kbd; + +void +kbdslave(void *a) +{ + char b; + + USED(a); + for(;;) { + b = readkbd(); + if(kbd.raw == 0) + write(1, &b, 1); + qproduce(kbdq, &b, 1); + } + pexit("kbdslave", 0); +} + +void +gkbdputc(Queue *q, int ch) +{ + int n; + Rune r; + static uchar kc[5*UTFmax]; + static int nk, collecting = 0; + char buf[UTFmax]; + + r = ch; + if(r == Latin) { + collecting = 1; + nk = 0; + return; + } + if(collecting) { + int c; + nk += runetochar((char*)&kc[nk], &r); + c = latin1(kc, nk); + if(c < -1) /* need more keystrokes */ + return; + collecting = 0; + if(c == -1) { /* invalid sequence */ + qproduce(q, kc, nk); + return; + } + r = (Rune)c; + } + n = runetochar(buf, &r); + if(n == 0) + return; + /* if(!isdbgkey(r)) */ + qproduce(q, buf, n); +} + +void +consinit(void) +{ + kbdq = qopen(512, 0, 0, 0); + if(kbdq == 0) + panic("no memory"); + lineq = qopen(512, 0, 0, 0); + if(lineq == 0) + panic("no memory"); + gkbdq = qopen(512, 0, 0, 0); + if(gkbdq == 0) + panic("no memory"); + randominit(); +} + +/* + * return true if current user is eve + */ +int +iseve(void) +{ + return strcmp(eve, up->env->user) == 0; +} + +Chan* +consattach(char *spec) +{ + static int kp; + + if (kp == 0 && !dflag) { + kproc("kbd", kbdslave, 0, 0); + kp = 1; + } + return devattach('c', spec); +} + +static Walkqid* +conswalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, contab, nelem(contab), devgen); +} + +int +consstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, contab, nelem(contab), devgen); +} + +Chan* +consopen(Chan *c, int omode) +{ + c = devopen(c, omode, contab, nelem(contab), devgen); + switch((ulong)c->qid.path) { + case Qconsctl: + incref(&kbd.ctl); + break; +#ifdef NOTYET + case Qkeyboard: + if((omode & 3) != OWRITE) { + qlock(&kbd.q); + kbd.kbdr++; + /* flushkbdline(kbdq); */ + kbd.raw = 1; + qunlock(&kbd.q); + } + break; +#endif + case Qpointer: + if(incref(&kbd.ptr) != 1){ + decref(&kbd.ptr); + c->flag &= ~COPEN; + error(Einuse); + } + break; + case Qscancode: + qlock(&kbd.gq); + if(gkscanq || !gkscanid) { + qunlock(&kbd.q); + c->flag &= ~COPEN; + if(gkscanq) + error(Einuse); + else + error(Ebadarg); + } + gkscanq = qopen(256, 0, nil, nil); + qunlock(&kbd.gq); + break; + case Qkprint: + wlock(&kprintq.l); + if(kprintq.q != nil){ + wunlock(&kprintq.l); + c->flag &= ~COPEN; + error(Einuse); + } + kprintq.q = qopen(32*1024, Qcoalesce, nil, nil); + if(kprintq.q == nil){ + wunlock(&kprintq.l); + c->flag &= ~COPEN; + error(Enomem); + } + qnoblock(kprintq.q, 1); + wunlock(&kprintq.l); + break; + case Qcaphash: + if(incref(&capopen) != 1){ + decref(&capopen); + c->flag &= ~COPEN; + error(Einuse); + } + break; + } + return c; +} + +void +consclose(Chan *c) +{ + if((c->flag & COPEN) == 0) + return; + + switch((ulong)c->qid.path) { + case Qconsctl: + if(decref(&kbd.ctl) == 0) + kbd.raw = 0; + break; + case Qkeyboard: + if(c->mode != OWRITE) { + qlock(&kbd.q); + --kbd.kbdr; + if(kbd.kbdr == 0) + kbd.raw = 0; + qunlock(&kbd.q); + } + break; + case Qpointer: + decref(&kbd.ptr); + break; + case Qscancode: + qlock(&kbd.gq); + if(gkscanq) { + qfree(gkscanq); + gkscanq = 0; + } + qunlock(&kbd.gq); + break; + case Qkprint: + wlock(&kprintq.l); + qfree(kprintq.q); + kprintq.q = nil; + wunlock(&kprintq.l); + break; + case Qcaphash: + decref(&capopen); + break; + } +} + +long +consread(Chan *c, void *xbuf, long n, vlong offset) +{ + int i, l, ch, eol; + Pointer m; + char *p, tmp[64]; + char *cbuf = xbuf; + + if(c->qid.type & QTDIR) + return devdirread(c, xbuf, n, contab, nelem(contab), devgen); + + switch((ulong)c->qid.path) { + default: + error(Egreg); + case Qsysctl: + return readstr(offset, xbuf, n, VERSION); + case Qsysname: + if(ossysname == nil) + return 0; + return readstr(offset, xbuf, n, ossysname); + case Qrandom: + return randomread(xbuf, n); + case Qnotquiterandom: + pseudoRandomBytes(xbuf, n); + return n; + case Qpin: + p = "pin set"; + if(up->env->pgrp->pin == Nopin) + p = "no pin"; + return readstr(offset, xbuf, n, p); + case Quser: + return readstr(offset, xbuf, n, up->env->user); + case Qjit: + snprint(tmp, sizeof(tmp), "%d", cflag); + return readstr(offset, xbuf, n, tmp); + case Qtime: + snprint(tmp, sizeof(tmp), "%.lld", timeoffset + osusectime()); + return readstr(offset, xbuf, n, tmp); + case Qdrivers: + p = malloc(READSTR); + if(p == nil) + error(Enomem); + if(waserror()){ + free(p); + nexterror(); + } + l = 0; + for(i = 0; devtab[i] != nil; i++) + l += snprint(p+l, READSTR-l, "#%C %s\n", devtab[i]->dc, devtab[i]->name); + n = readstr(offset, xbuf, n, p); + free(p); + poperror(); + return n; + case Qmemory: + return poolread(xbuf, n, offset); + + case Qnull: + return 0; + case Qmsec: + return readnum(offset, xbuf, n, osmillisec(), NUMSIZE); + case Qcons: + qlock(&kbd.q); + if(waserror()){ + qunlock(&kbd.q); + nexterror(); + } + + if(dflag) + error(Enonexist); + + if(kbd.raw) { + if(qcanread(lineq)) + n = qread(lineq, xbuf, 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*)xbuf; + } + } 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, xbuf, n); + } + qunlock(&kbd.q); + poperror(); + return n; + case Qscancode: + if(offset == 0) + return readstr(0, xbuf, n, gkscanid); + else + return qread(gkscanq, xbuf, n); + case Qkeyboard: + return qread(gkbdq, xbuf, n); + case Qpointer: + m = mouseconsume(); + l = sprint(tmp, "m%11d %11d %11d %11lud ", m.x, m.y, m.b, m.msec); + if (n < l) + n = l; + memmove(xbuf, tmp, n); + return n; + case Qkprint: + rlock(&kprintq.l); + if(waserror()){ + runlock(&kprintq.l); + nexterror(); + } + n = qread(kprintq.q, xbuf, n); + poperror(); + runlock(&kprintq.l); + return n; + } +} + +long +conswrite(Chan *c, void *va, long count, vlong offset) +{ + char buf[128], *p; + int x, y; + + USED(offset); + + if(c->qid.type & QTDIR) + error(Eperm); + + switch((ulong)c->qid.path) { + default: + error(Egreg); + case Qcons: + if(canrlock(&kprintq.l)){ + if(kprintq.q != nil){ + if(waserror()){ + runlock(&kprintq.l); + nexterror(); + } + qwrite(kprintq.q, va, count); + poperror(); + runlock(&kprintq.l); + return count; + } + runlock(&kprintq.l); + } + return write(1, va, count); + case Qsysctl: + return sysconwrite(va, count); + case Qconsctl: + if(count >= sizeof(buf)) + count = sizeof(buf)-1; + strncpy(buf, va, count); + buf[count] = 0; + if(strncmp(buf, "rawon", 5) == 0) { + kbd.raw = 1; + return count; + } + else + if(strncmp(buf, "rawoff", 6) == 0) { + kbd.raw = 0; + return count; + } + error(Ebadctl); + case Qkeyboard: + for(x=0; x<count; ) { + Rune r; + x += chartorune(&r, &((char*)va)[x]); + gkbdputc(gkbdq, r); + } + return count; + case Qpointer: + if(count > sizeof buf-1) + count = sizeof buf -1; + memmove(buf, va, count); + buf[count] = 0; + p = nil; + x = strtoul(buf+1, &p, 0); + if(p == nil || p == buf+1) + error(Eshort); + y = strtoul(p, 0, 0); + setpointer(x, y); + return count; + case Qnull: + return count; + case Qpin: + if(up->env->pgrp->pin != Nopin) + error("pin already set"); + if(count >= sizeof(buf)) + count = sizeof(buf)-1; + strncpy(buf, va, count); + buf[count] = '\0'; + up->env->pgrp->pin = atoi(buf); + return count; + case Qtime: + if(count >= sizeof(buf)) + count = sizeof(buf)-1; + strncpy(buf, va, count); + buf[count] = '\0'; + timeoffset = strtoll(buf, 0, 0)-osusectime(); + return count; + case Quser: + if(count >= sizeof(buf)) + count = sizeof(buf)-1; + strncpy(buf, va, count); + buf[count] = '\0'; + if(strcmp(up->env->user, eve) != 0) + error(Eperm); + setid(buf, 0); + return count; + case Qcaphash: + if(capwritehash(va, count) < 0) + error(Ebadarg); + return count; + case Qcapuse: + if(capwriteuse(va, count) < 0) + error(Eperm); + return count; + case Qjit: + if(count >= sizeof(buf)) + count = sizeof(buf)-1; + strncpy(buf, va, count); + buf[count] = '\0'; + x = atoi(buf); + if (x < 0 || x > 9) + error(Ebadarg); + cflag = x; + return count; + case Qsysname: + if(count >= sizeof(buf)) + count = sizeof(buf)-1; + strncpy(buf, va, count); + buf[count] = '\0'; + kstrdup(&ossysname, buf); + return count; + } + return 0; +} + +static Rb *rp; + +int +rbnotfull(void *v) +{ + int i; + + USED(v); + i = rp->wp - rp->rp; + if(i < 0) + i += sizeof(rp->buf); + return i < rp->target; +} + +static int +rbnotempty(void *v) +{ + USED(v); + return rp->wp != rp->rp; +} + +/* + * spin counting up + */ +void +genrandom(void *v) +{ + USED(v); + + osspin(&rp->producer); +} + +/* + * produce random bits in a circular buffer + */ +static void +randomclock(void *v) +{ + uchar *p; + + USED(v); + + for(;; osmillisleep(20)){ + while(!rbnotfull(0)){ + rp->filled = 1; + Sleep(&rp->clock, rbnotfull, 0); + } + + if(rp->randomcount == 0) + continue; + rp->bits = (rp->bits<<2) ^ (rp->randomcount&3); + rp->randomcount = 0; + rp->next += 2; + if(rp->next != 8) + continue; + + rp->next = 0; + *rp->wp ^= rp->bits ^ *rp->rp; + p = rp->wp+1; + if(p == rp->ep) + p = rp->buf; + rp->wp = p; + + if(rp->wakeme) + Wakeup(&rp->consumer); + } +} + +static void +randominit(void) +{ + rp=osraninit(); + rp->target = 16; + rp->ep = rp->buf + sizeof(rp->buf); + rp->rp = rp->wp = rp->buf; +} + +/* + * consume random bytes from a circular buffer + */ +static ulong +randomread(void *xp, ulong n) +{ + int i, sofar; + uchar *e, *p; + // ulong x; + + p = xp; + + if(waserror()){ + qunlock(&rp->l); + nexterror(); + } + + qlock(&rp->l); + if(!rp->kprocstarted){ + rp->kprocstarted = 1; + kproc("genrand", genrandom, 0, 0); + kproc("randomclock", randomclock, 0, 0); + } + + for(sofar = 0; sofar < n;){ + if(!rbnotempty(0)){ + rp->wakeme = 1; + Wakeup(&rp->clock); + oswakeupproducer(&rp->producer); + Sleep(&rp->consumer, rbnotempty, 0); + rp->wakeme = 0; + continue; + } + // x = rp->randn*1103515245 ^ *rp->rp; + // *(p+(sofar++)) = rp->randn = x; + *(p+(sofar++)) = *rp->rp; + e = rp->rp + 1; + if(e == rp->ep) + e = rp->buf; + rp->rp = e; + } + if(rp->filled && rp->wp == rp->rp){ + i = 2*rp->target; + if(i > sizeof(rp->buf) - 1) + i = sizeof(rp->buf) - 1; + rp->target = i; + rp->filled = 0; + } + qunlock(&rp->l); + poperror(); + + Wakeup(&rp->clock); + oswakeupproducer(&rp->producer); + + return n; +} + +static int +sysconwrite(void *va, ulong count) +{ + char *arg = (char*) va; + if(count>=6 && strncmp(arg, "reboot", 6) == 0) { + osreboot(rebootargv[0], rebootargv); + error("reboot not supported"); + return 0; + } else if(count>=4 && strncmp(arg, "halt", 4) == 0) + cleanexit(0); + else if(count>=6 && strncmp(arg, "broken", 6) == 0) + keepbroken = 1; + else if(count>=8 && strncmp(arg, "nobroken", 8) == 0) + keepbroken = 0; + else + error(Ebadarg); + return count; +} + +typedef struct Ptrevent Ptrevent; + +struct Ptrevent { + int x; + int y; + int b; + ulong msec; +}; + +enum { + Nevent = 16 /* enough for some */ +}; + +static struct { + int rd; + int wr; + Ptrevent clicks[Nevent]; + Rendez r; + int full; + int put; + int get; +} ptrq; + +static Pointer mouse = {-32768,-32768,0}; + +void +mouseproduce(Pointer m) +{ + int lastb; + Ptrevent e; + + e.x = m.x; + e.y = m.y; + e.b = m.b; + e.msec = osmillisec(); + lastb = mouse.b; + mouse.x = m.x; + mouse.y = m.y; + mouse.b = m.b; + mouse.msec = e.msec; + if(!ptrq.full && lastb != m.b){ + ptrq.clicks[ptrq.wr] = e; + if(++ptrq.wr == Nevent) + ptrq.wr = 0; + if(ptrq.wr == ptrq.rd) + ptrq.full = 1; + } + mouse.modify = 1; + ptrq.put++; + Wakeup(&ptrq.r); +/* drawactive(1); */ +} + +static int +ptrqnotempty(void *a) +{ + USED(a); + return ptrq.full || ptrq.put != ptrq.get; +} + +Pointer +mouseconsume(void) +{ + Pointer m; + Ptrevent e; + + Sleep(&ptrq.r, ptrqnotempty, 0); + ptrq.full = 0; + ptrq.get = ptrq.put; + if(ptrq.rd != ptrq.wr){ + e = ptrq.clicks[ptrq.rd]; + if(++ptrq.rd >= Nevent) + ptrq.rd = 0; + memset(&m, 0, sizeof(m)); + m.x = e.x; + m.y = e.y; + m.b = e.b; + m.msec = e.msec; + }else + m = mouse; + return m; +} + +Dev consdevtab = { + 'c', + "cons", + + consinit, + consattach, + conswalk, + consstat, + consopen, + devcreate, + consclose, + consread, + devbread, + conswrite, + devbwrite, + devremove, + devwstat +}; diff --git a/emu/port/devdraw.c b/emu/port/devdraw.c new file mode 100644 index 00000000..d267f275 --- /dev/null +++ b/emu/port/devdraw.c @@ -0,0 +1,2024 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +#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 q; + 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; /* compositing operator - SoverD by default */ +}; + +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; + Memimage *screenimage; /* accessed by some win-*.c */ +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 int drawclientop(Client*); + +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[] = "out of memory: image"; +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 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"; + +static int +drawgen(Chan *c, char *name, Dirtab *tab, int x, int s, Dir *dp) +{ + int t; + Qid q; + ulong path; + Client *cl; + + USED(name); + USED(tab); + USED(x); + 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 *l, Rectangle r, void *v) +{ + Refx *x; + DImage *d; + Client *c; + Refresh *ref; + + USED(l); + 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(Memimage *dst, Rectangle r) +{ + Memlayer *l; + + if(dst == screenimage){ + combinerect(&flushrect, r); + 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); +} + +static +void +drawflush(void) +{ + if(flushrect.min.x < flushrect.max.x) + flushmemscreen(flushrect); + flushrect = Rect(10000, 10000, -10000, -10000); +} + +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) + dstflush(l->layer->screen->image, 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); +} + +Point +drawchar(Memimage *dst, Point p, Memimage *src, Point *sp, DImage *font, int index, int op) +{ + FChar *fc; + Rectangle r; + Point sp1; + + 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; + 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; + Rectangle r; + + if(screenimage != nil) + return 1; + + memimageinit(); + screendata.base = nil; + screendata.bdata = attachscreen(&r, &chan, &depth, &width, &sdraw.softscreen); + if(screendata.bdata == nil) + return 0; + screendata.ref = 1; + + screenimage = allocmemimaged(r, chan, &screendata); + if(screenimage == nil){ + /* RSC: BUG: detach screen */ + return 0; + } + + screenimage->width = width; + screenimage->clipr = r; + return 1; +} + +void +deletescreenimage(void) +{ + qlock(&sdraw.q); + /* RSC: BUG: detach screen */ + if(screenimage) + freememimage(screenimage); + screenimage = nil; + qunlock(&sdraw.q); +} + +Chan* +drawattach(char *spec) +{ + qlock(&sdraw.q); + if(!initscreenimage()){ + qunlock(&sdraw.q); + error("no frame buffer"); + } + qunlock(&sdraw.q); + 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) + return devopen(c, omode, 0, 0, drawgen); + + qlock(&sdraw.q); + if(waserror()){ + qunlock(&sdraw.q); + 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.q); + 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.q); + if(waserror()){ + qunlock(&sdraw.q); + 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.q); + 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]; + + USED(offset); + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, drawgen); + cl = drawclient(c); + qlock(&sdraw.q); + if(waserror()){ + qunlock(&sdraw.q); + 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: +#ifdef COLORMAP + 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); +#else + n = 0; +#endif + 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.q); + if(waserror()){ + qlock(&sdraw.q); /* restore lock for waserror() above */ + nexterror(); + } + Sleep(&cl->refrend, drawrefactive, cl); + poperror(); + qlock(&sdraw.q); + } + 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.q); + 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 off) +{ + char buf[128], *fields[4], *q; + Client *cl; + int i, m, red, green, blue, x; + ulong offset = off; + + USED(offset); + if(c->qid.type & QTDIR) + error(Eisdir); + cl = drawclient(c); + qlock(&sdraw.q); + if(waserror()){ + drawwakeall(); + qunlock(&sdraw.q); + nexterror(); + } + switch(QID(c->qid)){ + case Qctl: + if(n != 4) + error("unknown draw control request"); + cl->infoid = BGLONG((uchar*)a); + break; + + case Qcolormap: +#ifdef COLORMAP + 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); + } +#else + n = 0; +#endif + break; + + case Qdata: + drawmesg(cl, a, n); + drawwakeall(); + break; + + default: + error(Ebadusefd); + } + qunlock(&sdraw.q); + 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); USED(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, op, repl, m, y, dstid, scrnid, ni, ci, j, nw, e0, e1, ox, oy, esize, oesize, 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->env->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 = nil; + 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); + dstflush(l->layer->screen->image, 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("cannot 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); + 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(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); + 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 & (1<<31)){ + if((ox & (1<<30)) == 0) + ox &= ~(1<<31); + memarc(dst, p, e0, e1, c, src, sp, ox, oy, op); + }else + memellipse(dst, p, e0, e1, c, src, sp, op); + dstflush(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); + dst = drawimage(client, a+1); + if(dstid == 0 || dst == screenimage) + error("cannot use display as font"); + font = drawlookup(client, dstid, 1); + if(font == 0) + error(Enodrawimage); + if(font->image->layer) + error("cannot 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(Enomem); + 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, SoverD); + 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); + 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(dst == screenimage || dst->layer!=nil){ + /* BUG: this is terribly inefficient: update maximal containing rect*/ + r = memlinebbox(p, q, e0, e1, j); + dstflush(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: cannot 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){ + dstflush(dst->layer->screen->image, r); + dstflush(dst->layer->screen->image, 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); + 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(dst == screenimage || (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(dst, r); + } + pp[y] = p; + } + if (y == 1) + dstflush(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); + 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); + 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, q, src, &sp, font, ci, op); + u += 2; + } + dst->clipr = clipr; + p.y -= font->ascent; + dstflush(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++) + dstflush(lp[j]->layer->screen->image, 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); + 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(dst, r); + m += y; + continue; + } + } + poperror(); +} + +int +drawlsetrefresh(ulong qidpath, int id, void *reffn, void *refx) +{ + DImage *d; + Memimage *i; + Client *client; + + client = drawclientofpath(qidpath); + if(client == 0) + return 0; + d = drawlookup(client, id, 0); + if(d == nil) + return 0; + i = d->image; + if(i->layer == nil) + return 0; + return memlsetrefresh(i, reffn, refx); +} + +void +drawqlock(void) +{ + qlock(&sdraw.q); +} + +void +drawqunlock(void) +{ + qunlock(&sdraw.q); +} + +void +drawxflush(Rectangle r) /* used by X11 only */ +{ + qlock(&sdraw.q); + flushmemscreen(r); + qunlock(&sdraw.q); +} + +void +interf(void) +{ + /* force it to load */ + drawreplxy(0, 0, 0); +} + +Dev drawdevtab = { + 'i', + "draw", + + devinit, + drawattach, + drawwalk, + drawstat, + drawopen, + devcreate, + drawclose, + drawread, + devbread, + drawwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/emu/port/devdup.c b/emu/port/devdup.c new file mode 100644 index 00000000..7796a8f9 --- /dev/null +++ b/emu/port/devdup.c @@ -0,0 +1,150 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "interp.h" + +/* Qid is (2*fd + (file is ctl))+1 */ + +static int +dupgen(Chan *c, char *name, Dirtab *tab, int ntab, int s, Dir *dp) +{ + Fgrp *fgrp = up->env->fgrp; + Chan *f; + static int perm[] = { 0400, 0200, 0600, 0 }; + int p; + Qid q; + + USED(name); USED(tab); USED(ntab); + if(s == DEVDOTDOT){ + devdir(c, c->qid, ".", 0, eve, DMDIR|0555, dp); + return 1; + } + if(s == 0) + return 0; + s--; + if(s/2 > fgrp->maxfd) + return -1; + if((f=fgrp->fd[s/2]) == nil) + return 0; + if(s & 1){ + p = 0400; + sprint(up->genbuf, "%dctl", s/2); + }else{ + p = perm[f->mode&3]; + sprint(up->genbuf, "%d", s/2); + } + mkqid(&q, s+1, 0, QTFILE); + devdir(c, q, up->genbuf, 0, eve, p, dp); + return 1; +} + +static Chan* +dupattach(char *spec) +{ + return devattach('d', spec); +} + +static Walkqid* +dupwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, dupgen); +} + +static int +dupstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, nil, 0, dupgen); +} + +static Chan* +dupopen(Chan *c, int omode) +{ + Chan *f; + int fd, twicefd; + + if(c->qid.type & QTDIR){ + if(omode != 0) + error(Eisdir); + c->mode = 0; + c->flag |= COPEN; + c->offset = 0; + return c; + } + if(c->qid.type & QTAUTH) + error(Eperm); + twicefd = c->qid.path - 1; + fd = twicefd/2; + if((twicefd & 1)){ + /* ctl file */ + f = c; + f->mode = openmode(omode); + f->flag |= COPEN; + f->offset = 0; + }else{ + /* fd file */ + f = fdtochan(up->env->fgrp, fd, openmode(omode), 0, 1); + cclose(c); + } + if(omode & OCEXEC) + f->flag |= CCEXEC; + return f; +} + +static void +dupclose(Chan *c) +{ + USED(c); +} + +static long +dupread(Chan *c, void *va, long n, vlong offset) +{ + char *a = va; + char buf[256]; + int fd, twicefd; + + if(c->qid.type == QTDIR) + return devdirread(c, a, n, nil, 0, dupgen); + twicefd = c->qid.path - 1; + fd = twicefd/2; + if(twicefd & 1){ + c = fdtochan(up->env->fgrp, fd, -1, 0, 1); + if(waserror()){ + cclose(c); + nexterror(); + } + progfdprint(c, fd, 0, buf, sizeof buf); + poperror(); + cclose(c); + return readstr((ulong)offset, va, n, buf); + } + panic("dupread"); + return 0; +} + +static long +dupwrite(Chan *c, void *a, long n, vlong o) +{ + USED(c); USED(a); USED(n); USED(o); + panic("dupwrite"); + return 0; /* not reached */ +} + +Dev dupdevtab = { + 'd', + "dup", + + devinit, + dupattach, + dupwalk, + dupstat, + dupopen, + devcreate, + dupclose, + dupread, + devbread, + dupwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/emu/port/devdynld.c b/emu/port/devdynld.c new file mode 100644 index 00000000..68084707 --- /dev/null +++ b/emu/port/devdynld.c @@ -0,0 +1,357 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include <a.out.h> +#include <dynld.h> + +#define DBG if(1) print + +extern ulong ndevs; + +enum +{ + Qdir, + Qdynld, + Qdynsyms, + + DEVCHAR = 'L', +}; + +static Dirtab dltab[] = +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "dynld", {Qdynld}, 0, 0644, + "dynsyms", {Qdynsyms}, 0, 0444, +}; + +enum +{ + DLdev, + DLudev, +}; + +static Cmdtab dlcmd[] = +{ + DLdev, "dev", 2, + DLudev, "udev", 2, +}; + +typedef struct Dyndev Dyndev; + +struct Dyndev +{ + char *path; + Dynobj *o; + Dev *dev; + Dyndev *next; +}; + +static Dyndev *loaded; +static QLock dllock; + +typedef struct Fd Fd; +struct Fd { + int fd; +}; + +static long +readfd(void *a, void *buf, long nbytes) +{ + return kread(((Fd*)a)->fd, buf, nbytes); +} + +static vlong +seekfd(void *a, vlong off, int t) +{ + return kseek(((Fd*)a)->fd, off, t); +} + +static void +errfd(char *s) +{ + kstrcpy(up->env->errstr, s, ERRMAX); +} + +static void +dlfree(Dyndev *l) +{ + if(l != nil){ + free(l->path); + dynobjfree(l->o); + free(l); + } +} + +static Dyndev* +dlload(char *path, Dynsym *tab, int ntab) +{ + Fd f; + Dyndev *l; + + f.fd = kopen(path, OREAD); + if(f.fd < 0) + error("cannot open"); + if(waserror()){ + kclose(f.fd); + nexterror(); + } + l = mallocz(sizeof(Dyndev), 1); + if(l == nil) + error(Enomem); + if(waserror()){ + dlfree(l); + nexterror(); + } + l->path = strdup(path); + if(l->path == nil) + error(Enomem); + l->o = dynloadgen(&f, readfd, seekfd, errfd, tab, ntab, 0); + if(l->o == nil) + error(up->env->errstr); + poperror(); + poperror(); + kclose(f.fd); + return l; +} + +static void +devload(char *path) +{ + int i; + Dyndev *l; + Dev *dev; + char devname[32]; + + l = dlload(path, _exporttab, dyntabsize(_exporttab)); + if(waserror()){ + dlfree(l); + nexterror(); + } + snprint(devname, sizeof(devname), "%sdevtab", "XXX"); /* TO DO */ + dev = dynimport(l->o, devname, signof(*dev)); + if(dev == nil) + error("no devtab"); + if(devno(dev->dc, 1) >= 0) + error("device loaded"); + for(i = 0; devtab[i] != nil; i++) + ; + if(i >= ndevs || devtab[i+1] != nil) + error("device table full"); + l->dev = devtab[i] = dev; + dev->init(); + l->next = loaded; + loaded = l; + poperror(); +} + +static void +devunload(char *path) +{ + int i, dc; + Dyndev *l, **ll; + + dc = 0; + if(strlen(path) == 1) + dc = path[0]; + for(ll = &loaded; *ll != nil; ll = &(*ll)->next){ + if(path != nil && strcmp(path, (*ll)->path) == 0) + break; + if(dc != 0 && (*ll)->dev && dc == (*ll)->dev->dc) + break; + } + if((l = *ll) != nil){ + for(i = 0; i < ndevs; i++) + if(l->dev == devtab[i]){ + devtab[i] = nil; + break; + } +/* + if(l->dev) + l->dev->shutdown(); +*/ + *ll = l->next; + dlfree(l); + } +} + +static long +readdl(void *a, ulong n, ulong offset) +{ + char *p; + Dyndev *l; + int m, len; + + m = 0; + for(l = loaded; l != nil; l = l->next) + m++; + m *= 48; + p = malloc(m); + if(p == nil) + error(Enomem); + if(waserror()){ + free(p); + nexterror(); + } + *p = 0; + len = 0; + for(l = loaded; l != nil; l = l->next) + if(l->dev) + len += snprint(p+len, m-len, "#%C\t%.8p\t%.8lux\t%s\n", + l->dev->dc, l->o->base, l->o->size, l->dev->name); + n = readstr(offset, a, n, p); + poperror(); + free(p); + return n; +} + +static long +readsyms(char *a, ulong n, ulong offset) +{ + char *p; + Dynsym *t; + long l, nr; + + p = malloc(READSTR); + if(p == nil) + error(Enomem); + if(waserror()){ + free(p); + nexterror(); + } + nr = 0; + for(t = _exporttab; n > 0 && t->name != nil; t++){ + l = snprint(p, READSTR, "%.8lux %.8lux %s\n", t->addr, t->sig, t->name); + if(offset >= l){ + offset -= l; + continue; + } + l = readstr(offset, a, n, p); + offset = 0; + n -= l; + a += l; + nr += l; + } + poperror(); + free(p); + return nr; +} + +static Chan* +dlattach(char *spec) +{ + return devattach(DEVCHAR, spec); +} + +static Walkqid* +dlwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, dltab, nelem(dltab), devgen); +} + +static int +dlstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, dltab, nelem(dltab), devgen); +} + +static Chan* +dlopen(Chan *c, int omode) +{ + return devopen(c, omode, dltab, nelem(dltab), devgen); +} + +static void +dlclose(Chan *c) +{ + USED(c); +} + +static long +dlread(Chan *c, void *a, long n, vlong voffset) +{ + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, dltab, nelem(dltab), devgen); + case Qdynld: + return readdl(a, n, (ulong)voffset); + case Qdynsyms: + return readsyms(a, n, (ulong)voffset); + default: + error(Egreg); + } + return n; +} + +static long +dlwrite(Chan *c, void *a, long n, vlong voffset) +{ + Cmdbuf *cmd; + Cmdtab *ct; + + USED(voffset); + switch((ulong)c->qid.path){ + case Qdynld: + cmd = parsecmd(a, n); + qlock(&dllock); + if(waserror()){ + qunlock(&dllock); + free(cmd); + nexterror(); + } + ct = lookupcmd(cmd, dlcmd, nelem(dlcmd)); + switch(ct->index){ + case DLdev: + devload(cmd->f[1]); + break; + case DLudev: + devunload(cmd->f[1]); + break; + } + poperror(); + qunlock(&dllock); + free(cmd); + break; + default: + error(Egreg); + } + return n; +} + +Dev dynlddevtab = { + DEVCHAR, + "dynld", + + devinit, + dlattach, + dlwalk, + dlstat, + dlopen, + devcreate, + dlclose, + dlread, + devbread, + dlwrite, + devbwrite, + devremove, + devwstat, +}; + +/* auxiliary routines for dynamic loading of C modules */ + +Dynobj* +dynld(int fd) +{ + Fd f; + + f.fd = fd; + return dynloadgen(&f, readfd, seekfd, errfd, _exporttab, dyntabsize(_exporttab), 0); +} + +int +dynldable(int fd) +{ + Fd f; + + f.fd = fd; + return dynloadable(&f, readfd, seekfd); +} diff --git a/emu/port/devdynldx.c b/emu/port/devdynldx.c new file mode 100644 index 00000000..738b9ed5 --- /dev/null +++ b/emu/port/devdynldx.c @@ -0,0 +1,357 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include <a.out.h> +#include <dynld.h> + +/* + * TO DO + * - dynamic allocation of Dev.dc + * - inter-module dependencies + * - reference count on Dev to allow error("inuse") [or how else to do it] + * - is Dev.shutdown the right function to call? Dev.config perhaps? + */ + +#define DBG if(1) print + +extern ulong ndevs; + +enum +{ + Qdir, + Qdynld, + Qdynsyms, + + DEVCHAR = 'L', +}; + +static Dirtab dltab[] = +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "dynld", {Qdynld}, 0, 0644, + "dynsyms", {Qdynsyms}, 0, 0444, +}; + +typedef struct Dyndev Dyndev; + +struct Dyndev +{ + char* name; /* device name (eg, "dynld") */ + char* tag; /* version tag (eg, MD5 or SHA1 hash of content) */ + char* path; /* file from whence it came */ + Dynobj* o; + Dev* dev; + Dyndev* next; +}; + +static Dyndev *loaded; +static QLock dllock; + +static Dyndev** finddyndev(char*); +static int matched(Dyndev*, char*, char*); + +extern Dynobj* kdynloadfd(int, Dynsym*, int, ulong); + +static void +dlfree(Dyndev *l) +{ + if(l != nil){ + free(l->tag); + free(l->name); + free(l->path); + dynobjfree(l->o); + free(l); + } +} + +static Dyndev* +dlload(char *path, Dynsym *tab, int ntab) +{ + Dyndev *l; + int fd; + + /* in Plan 9, would probably use Chan* interface here */ + fd = kopen(path, OREAD); + if(fd < 0) + error("cannot open"); + if(waserror()){ + kclose(fd); + nexterror(); + } + l = mallocz(sizeof(Dyndev), 1); + if(l == nil) + error(Enomem); + if(waserror()){ + dlfree(l); + nexterror(); + } + l->path = strdup(path); + if(l->path == nil) + error(Enomem); + l->o = kdynloadfd(fd, tab, ntab, 0); + if(l->o == nil) + error(up->env->errstr); + poperror(); + poperror(); + kclose(fd); + return l; +} + +static void +devload(char *name, char *path, char *tag) +{ + int i; + Dyndev *l, **lp; + Dev *dev; + char tabname[32]; + + lp = finddyndev(name); + if(*lp != nil) + error("already loaded"); /* i'm assuming the name (eg, "cons") is to be unique */ + l = dlload(path, _exporttab, dyntabsize(_exporttab)); + if(waserror()){ + dlfree(l); + nexterror(); + } + snprint(tabname, sizeof(tabname), "%sdevtab", name); + dev = dynimport(l->o, tabname, signof(*dev)); + if(dev == nil) + errorf("can't find %sdevtab in module", name); + kstrdup(&l->name, name); + kstrdup(&l->tag, tag != nil? tag: ""); + if(dev->name == nil) + dev->name = l->name; + else if(strcmp(dev->name, l->name) != 0) + errorf("module file has device %s", dev->name); + /* TO DO: allocate dev->dc dynamically (cf. brucee's driver) */ + if(devno(dev->dc, 1) >= 0) + errorf("devchar %C already used", dev->dc); + for(i = 0; devtab[i] != nil; i++) + ; + if(i >= ndevs || devtab[i+1] != nil) + error("device table full"); +#ifdef NATIVE + i = splhi(); + dev->reset(); + splx(i); +#endif + dev->init(); + l->dev = devtab[i] = dev; + l->next = loaded; + loaded = l; /* recently loaded ones first: good unload order? */ + poperror(); +} + +static Dyndev** +finddyndev(char *name) +{ + Dyndev *l, **lp; + + for(lp = &loaded; (l = *lp) != nil; lp = &l->next) + if(strcmp(l->name, name) == 0) + break; + return lp; +} + +static int +matched(Dyndev *l, char *path, char *tag) +{ + if(path != nil && strcmp(l->path, path) != 0) + return 0; + if(tag != nil && strcmp(l->tag, tag) != 0) + return 0; + return 1; +} + +static void +devunload(char *name, char *path, char *tag) +{ + int i; + Dyndev *l, **lp; + + lp = finddyndev(name); + l = *lp; + if(l == nil) + error("not loaded"); + if(!matched(l, path, tag)) + error("path/tag mismatch"); + for(i = 0; i < ndevs; i++) + if(l->dev == devtab[i]){ + devtab[i] = nil; /* TO DO: ensure driver is not currently in use */ + break; + } +#ifdef NATIVE + l->dev->shutdown(); +#endif + *lp = l->next; + dlfree(l); +} + +static long +readdynld(void *a, ulong n, ulong offset) +{ + char *p; + Dyndev *l; + int m, len; + + m = 0; + for(l = loaded; l != nil; l = l->next) + m += 48 + strlen(l->name) + strlen(l->path) + strlen(l->tag); + p = malloc(m); + if(p == nil) + error(Enomem); + if(waserror()){ + free(p); + nexterror(); + } + *p = 0; + len = 0; + for(l = loaded; l != nil; l = l->next) + if(l->dev) + len += snprint(p+len, m-len, "#%C\t%.8p\t%.8lud\t%q\t%q\t%q\n", + l->dev->dc, l->o->base, l->o->size, l->name, l->path, l->tag); + n = readstr(offset, a, n, p); + poperror(); + free(p); + return n; +} + +static long +readsyms(char *a, ulong n, ulong offset) +{ + char *p; + Dynsym *t; + long l, nr; + + p = malloc(READSTR); + if(p == nil) + error(Enomem); + if(waserror()){ + free(p); + nexterror(); + } + nr = 0; + for(t = _exporttab; n > 0 && t->name != nil; t++){ + l = snprint(p, READSTR, "%.8lux %.8lux %s\n", t->addr, t->sig, t->name); + if(offset >= l){ + offset -= l; + continue; + } + l = readstr(offset, a, n, p); + offset = 0; + n -= l; + a += l; + nr += l; + } + poperror(); + free(p); + return nr; +} + +static Chan* +dlattach(char *spec) +{ + return devattach(DEVCHAR, spec); +} + +static Walkqid* +dlwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, dltab, nelem(dltab), devgen); +} + +static int +dlstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, dltab, nelem(dltab), devgen); +} + +static Chan* +dlopen(Chan *c, int omode) +{ + return devopen(c, omode, dltab, nelem(dltab), devgen); +} + +static void +dlclose(Chan *c) +{ + USED(c); +} + +static long +dlread(Chan *c, void *a, long n, vlong voffset) +{ + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, dltab, nelem(dltab), devgen); + case Qdynld: + return readdynld(a, n, voffset); + case Qdynsyms: + return readsyms(a, n, voffset); + default: + error(Egreg); + } + return n; +} + +static long +dlwrite(Chan *c, void *a, long n, vlong voffset) +{ + Cmdbuf *cb; + char *name, *tag, *path; + + USED(voffset); + switch((ulong)c->qid.path){ + case Qdynld: + cb = parsecmd(a, n); + qlock(&dllock); + if(waserror()){ + qunlock(&dllock); + free(cb); + nexterror(); + } + if(cb->nf < 3 || strcmp(cb->f[1], "dev") != 0) /* only do devices */ + cmderror(cb, Ebadctl); + name = cb->f[2]; + path = nil; + if(cb->nf > 3) + path = cb->f[3]; + tag = nil; + if(cb->nf > 4) + tag = cb->f[4]; + if(strcmp(cb->f[0], "load") == 0){ + if(path == nil) + cmderror(cb, "missing load path"); + devload(name, path, tag); /* TO DO: remaining parameters might be dependencies? */ + }else if(strcmp(cb->f[0], "unload") == 0) + devunload(name, path, tag); + else + cmderror(cb, Ebadctl); + poperror(); + qunlock(&dllock); + free(cb); + break; + default: + error(Egreg); + } + return n; +} + +Dev dynlddevtab = { + DEVCHAR, + "dynld", + + devinit, + dlattach, + dlwalk, + dlstat, + dlopen, + devcreate, + dlclose, + dlread, + devbread, + dlwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/emu/port/deveia-bsd.c b/emu/port/deveia-bsd.c new file mode 100644 index 00000000..3640837d --- /dev/null +++ b/emu/port/deveia-bsd.c @@ -0,0 +1,95 @@ +/* + * BSD serial port control features not found in POSIX + * including modem line control and hardware flow control + */ + +static struct flagmap lines[] = { + {"cts", TIOCM_CTS}, + {"dsr", TIOCM_DSR}, + {"ring", TIOCM_RI}, + {"dcd", TIOCM_CD}, + {"dtr", TIOCM_DTR}, + {"rts", TIOCM_RTS}, + {0, -1} +}; + +static void +resxtra(int port, struct termios *ts) +{ + int fd = eia[port].fd; + + USED(ts); + + if(eia[port].dtr) + ioctl(fd, TIOCM_DTR, eia[port].dtr); + if(eia[port].rts) + ioctl(fd, TIOCM_RTS, eia[port].rts); + if(eia[port].cts) + ioctl(fd, TIOCM_CTS, eia[port].cts); +} + +static char * +rdxtra(int port, struct termios *ts, char *str) +{ + int fd = eia[port].fd; + int line; +// struct flagmap *lp; + char *s = str; + + USED(ts); + + if(ioctl(fd, TIOCMGET, &line) < 0) + oserror(); + +// for(lp = lines; lp->str; lp++) +// if(line&lp->flag) +// s += sprint(s, " %s", lp->str); + + return s; +} + +static char * +wrxtra(int port, struct termios *ts, char *cmd) +{ + int fd = eia[port].fd; + int n, r, flag, iocmd, *l; + + USED(ts); + + switch(*cmd) { + case 'D': + case 'd': + flag = TIOCM_DTR; + l = &eia[port].dtr; + break; + case 'R': + case 'r': + flag = TIOCM_RTS; + l = &eia[port].rts; + break; + case 'M': + case 'm': + flag = TIOCM_CTS; + l = &eia[port].cts; + break; + default: + return nil; + } + + n = atoi(cmd+1); + if(n) + iocmd = TIOCMBIS; + else + iocmd = TIOCMBIC; + + osenter(); + r = ioctl(fd, iocmd, &flag); + osleave(); + if(r < 0) + oserror(); + + eia[port].restore = 1; + *l = iocmd; + + return nil; +} diff --git a/emu/port/deveia-posix.c b/emu/port/deveia-posix.c new file mode 100644 index 00000000..e6e34bc1 --- /dev/null +++ b/emu/port/deveia-posix.c @@ -0,0 +1,463 @@ +/* + * Driver for POSIX serial ports + */ +#include "dat.h" +#include "fns.h" +#include "error.h" +#undef _POSIX_C_SOURCE /* for deveia-bsd.c */ +#include <sys/stat.h> +#include <termios.h> + +enum +{ + Devchar = 't', + + Ndataqid = 1, + Nctlqid, + Nstatqid, + Nqid = 3, /* number of QIDs */ + + CTLS= 023, + CTLQ= 021, + + Maxctl = 128, + Maxfield = 32 +}; + +/* + * Macros to manage QIDs + */ +#define NETTYPE(x) ((x)&0x0F) +#define NETID(x) ((x)>>4) +#define NETQID(i,t) (((i)<<4)|(t)) + +static Dirtab *eiadir; +static int ndir; + +static char Devname[] = "eia"; + +typedef struct Eia Eia; +struct Eia { + Ref r; + int fd; + int overrun; + int frame; + int restore; /* flag to restore prev. states */ + struct termios ts; + int dtr; + int rts; + int cts; +}; + +static Eia *eia; + +struct tcdef_t { + int val; + tcflag_t flag; +}; + +struct flagmap { + char* s; + tcflag_t flag; +}; + +static struct tcdef_t bps[]; + +static struct tcdef_t size[] = { + {5, CS5}, + {6, CS6}, + {7, CS7}, + {8, CS8}, + {-1, -1} +}; + +static char * +ftos(char *buf, struct tcdef_t *tbl, tcflag_t flag) +{ + for(; tbl->val >= 0; tbl++) + if(tbl->flag == flag){ + sprint(buf, "%d", tbl->val); + return buf; + } + return "unknown"; +} + +static tcflag_t +stof(struct tcdef_t *tbl, int val) +{ + for(; tbl->val >= 0 && tbl->val != val; tbl++) + {} + return tbl->flag; +} + +static char * +rdxtra(int port, struct termios *ts, char *str); /* non-POSIX extensions */ + +static long +rdstat(int port, void *buf, long n, ulong offset) +{ + int fd = eia[port].fd; + struct termios ts; + char str[Maxctl]; + char sbuf[20]; + char *s; + + if(tcgetattr(fd, &ts) < 0) + oserror(); + + s = str; + s += sprint(s, "opens %d ferr %d oerr %d baud %s", + eia[port].r.ref-1, eia[port].frame, eia[port].overrun, + ftos(sbuf, bps, (tcflag_t)cfgetospeed(&ts))); + s = rdxtra(port, &ts, s); + sprint(s, "\n"); + + return readstr(offset, buf, n, str); +} + +static char * +wrxtra(int port, struct termios *ts, char *cmd); /* non-POSIX extensions */ + +static void +wrctl(int port, char *cmd) +{ + struct termios ts; + char *xerr; + int r, nf, n, i; + char *f[Maxfield]; + int fd = eia[port].fd; + tcflag_t flag; + + if(tcgetattr(fd, &ts) < 0) { +Error: + oserror(); + } + + nf = tokenize(cmd, f, nelem(f)); + for(i = 0; i < nf; i++){ + if(strncmp(f[i], "break", 5) == 0){ + tcsendbreak(fd, 0); + continue; + } + n = atoi(f[i]+1); + switch(*f[i]) { + case 'F': + case 'f': + if(tcflush(fd, TCOFLUSH) < 0) + goto Error; + break; + case 'K': + case 'k': + if(tcsendbreak(fd, 0) < 0) + ; /* ignore it */ + break; + case 'H': + case 'h': + cfsetospeed(&ts, B0); + break; + case 'B': + case 'b': + flag = stof(bps, n); + if((int)flag == -1) + error(Ebadarg); + cfsetispeed(&ts, (speed_t)flag); + cfsetospeed(&ts, (speed_t)flag); + break; + case 'L': + case 'l': + flag = stof(size, n); + if((int)flag == -1) + error(Ebadarg); + ts.c_cflag &= ~CSIZE; + ts.c_cflag |= flag; + break; + case 'S': + case 's': + if(n == 1) + ts.c_cflag &= ~CSTOPB; + else if(n ==2) + ts.c_cflag |= CSTOPB; + else + error(Ebadarg); + break; + case 'P': + case 'p': + if(*(f[i]+1) == 'o') + ts.c_cflag |= PARENB|PARODD; + else if(*(f[i]+1) == 'e') { + ts.c_cflag |= PARENB; + ts.c_cflag &= ~PARODD; + } + else + ts.c_cflag &= ~PARENB; + break; + case 'X': + case 'x': + if(n == 0) + ts.c_iflag &= ~(IXON|IXOFF); + else + ts.c_iflag |= (IXON|IXOFF); + break; + case 'i': + case 'I': + /* enable fifo; ignore */ + break; + default: + if((xerr = wrxtra(port, &ts, f[i])) != nil) + error(xerr); + } + } + + osenter(); + r = tcsetattr(fd, TCSADRAIN, &ts); + osleave(); + if(r < 0) + goto Error; + eia[port].restore = 1; + eia[port].ts = ts; +} + +static void +eiainit(void) +{ + int i, nports; + Dirtab *dp; + struct stat sb; + +#ifdef buildsysdev + buildsysdev(); +#endif + + /* check to see which ports exist by trying to stat them */ + nports = 0; + for (i=0; i < nelem(sysdev); i++) { + if(stat(sysdev[i], &sb) < 0) + break; + + nports++; + } + + if (!nports) + return; + + ndir = Nqid*nports+1; + dp = eiadir = calloc(ndir, sizeof(Dirtab)); + if(dp == 0) + panic("eiainit"); + strcpy(dp->name, "."); + dp->qid.path = 0; + dp->qid.type = QTDIR; + dp->perm = DMDIR|0555; + dp++; + eia = calloc(nports, sizeof(Eia)); + if(eia == 0) + panic("eiainit"); + for(i = 0; i < nports; i++) { + sprint(dp->name, "%s%d", Devname, i); + dp->qid.path = NETQID(i, Ndataqid); + dp->perm = 0660; + dp++; + sprint(dp->name, "%s%dctl", Devname, i); + dp->qid.path = NETQID(i, Nctlqid); + dp->perm = 0660; + dp++; + sprint(dp->name, "%s%dstatus", Devname, i); + dp->qid.path = NETQID(i, Nstatqid); + dp->perm = 0660; + dp++; + eia[i].frame = eia[i].overrun = 0; + eia[i].restore = eia[i].dtr = eia[i].rts = eia[i].cts = 0; + } +} + +static Chan* +eiaattach(char *spec) +{ + if(eiadir == nil) + error(Enodev); + + return devattach(Devchar, spec); +} + +Walkqid* +eiawalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, eiadir, ndir, devgen); +} + +int +eiastat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, eiadir, ndir, devgen); +} + +static void +resxtra(int port, struct termios *ts); /* non-POSIX extensions */ + +static Chan* +eiaopen(Chan *c, int mode) +{ + int port = NETID(c->qid.path); + struct termios ts; + int r; + + c = devopen(c, mode, eiadir, ndir, devgen); + + switch(NETTYPE(c->qid.path)) { + case Nctlqid: + case Ndataqid: + case Nstatqid: + if(incref(&eia[port].r) != 1) + break; + + osenter(); + eia[port].fd = open(sysdev[port], O_RDWR); + osleave(); + if(eia[port].fd < 0) + oserror(); + + /* make port settings sane */ + if(tcgetattr(eia[port].fd, &ts) < 0) + oserror(); + ts.c_iflag = ts.c_oflag = ts.c_lflag = 0; + if(eia[port].restore) + ts = eia[port].ts; + else { + cfsetispeed(&ts, B9600); + cfsetospeed(&ts, B9600); + ts.c_iflag |= IGNPAR; + ts.c_cflag &= ~CSIZE; + ts.c_cflag |= CS8|CREAD; + ts.c_cflag &= ~(PARENB|PARODD); + ts.c_cc[VMIN] = 1; + ts.c_cc[VTIME] = 0; + } + osenter(); + r = tcsetattr(eia[port].fd, TCSANOW, &ts); + osleave(); + if(r < 0) + oserror(); + + if(eia[port].restore) + resxtra(port, &ts); + break; + } + return c; +} + +static void +eiaclose(Chan *c) +{ + int port = NETID(c->qid.path); + + if((c->flag & COPEN) == 0) + return; + + switch(NETTYPE(c->qid.path)) { + case Nctlqid: + case Ndataqid: + case Nstatqid: + if(decref(&eia[port].r) != 0) + break; + if(eia[port].fd >= 0) { + osenter(); + close(eia[port].fd); + osleave(); + } + break; + } + +} + +static long +eiaread(Chan *c, void *buf, long n, vlong offset) +{ + ssize_t cnt; + int port = NETID(c->qid.path); + + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, eiadir, ndir, devgen); + + switch(NETTYPE(c->qid.path)) { + case Ndataqid: + osenter(); + cnt = read(eia[port].fd, buf, n); + osleave(); + if(cnt == -1) + oserror(); + return cnt; + case Nctlqid: + return readnum(offset, buf, n, port, NUMSIZE); + case Nstatqid: + return rdstat(port, buf, n, offset); + } + + return 0; +} + +static long +eiawrite(Chan *c, void *buf, long n, vlong offset) +{ + ssize_t cnt; + char cmd[Maxctl]; + int port = NETID(c->qid.path); + + USED(offset); + + if(c->qid.type & QTDIR) + error(Eperm); + + switch(NETTYPE(c->qid.path)) { + case Ndataqid: + osenter(); + cnt = write(eia[port].fd, buf, n); + osleave(); + if(cnt == -1) + oserror(); + return cnt; + case Nctlqid: + if(n >= (long)sizeof(cmd)) + n = sizeof(cmd)-1; + memmove(cmd, buf, n); + cmd[n] = 0; + wrctl(port, cmd); + return n; + } + return 0; +} + +int +eiawstat(Chan *c, uchar *dp, int n) +{ + Dir d; + int i; + + if(strcmp(up->env->user, eve) != 0) + error(Eperm); + if(c->qid.type & QTDIR) + error(Eperm); + + n = convM2D(dp, n, &d, nil); + i = Nqid*NETID(c->qid.path)+NETTYPE(c->qid.path)-Ndataqid; + eiadir[i+1].perm = d.mode&0666; + return n; +} + +Dev eiadevtab = { + Devchar, + Devname, + + eiainit, + eiaattach, + eiawalk, + eiastat, + eiaopen, + devcreate, + eiaclose, + eiaread, + devbread, + eiawrite, + devbwrite, + devremove, + eiawstat +}; diff --git a/emu/port/devenv.c b/emu/port/devenv.c new file mode 100644 index 00000000..104d6344 --- /dev/null +++ b/emu/port/devenv.c @@ -0,0 +1,245 @@ +/* + * devenv - environment + */ +#include "dat.h" +#include "fns.h" +#include "error.h" + +static void envremove(Chan*); + +static int +envgen(Chan *c, char *name, Dirtab *d, int nd, int s, Dir *dp) +{ + Egrp *eg; + Evalue *e; + + USED(name); + USED(d); + USED(nd); + if(s == DEVDOTDOT){ + devdir(c, c->qid, "#e", 0, eve, DMDIR|0775, dp); + return 1; + } + eg = up->env->egrp; + qlock(&eg->l); + for(e = eg->entries; e != nil && s != 0; e = e->next) + s--; + if(e == nil) { + qunlock(&eg->l); + return -1; + } + /* make sure name string continues to exist after we release lock */ + kstrcpy(up->genbuf, e->var, sizeof up->genbuf); + devdir(c, e->qid, up->genbuf, e->len, eve, 0666, dp); + qunlock(&eg->l); + return 1; +} + +static Chan* +envattach(char *spec) +{ + return devattach('e', spec); +} + +static Walkqid* +envwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, envgen); +} + +static int +envstat(Chan *c, uchar *db, int n) +{ + if(c->qid.type & QTDIR) + c->qid.vers = up->env->egrp->vers; + return devstat(c, db, n, 0, 0, envgen); +} + +static Chan * +envopen(Chan *c, int mode) +{ + Egrp *eg; + Evalue *e; + + if(c->qid.type & QTDIR) { + if(mode != OREAD) + error(Eperm); + c->mode = mode; + c->flag |= COPEN; + c->offset = 0; + return c; + } + eg = up->env->egrp; + qlock(&eg->l); + for(e = eg->entries; e != nil; e = e->next) + if(e->qid.path == c->qid.path) + break; + if(e == nil) { + qunlock(&eg->l); + error(Enonexist); + } + if(mode == (OWRITE|OTRUNC) && e->val) { + free(e->val); + e->val = 0; + e->len = 0; + e->qid.vers++; + } + qunlock(&eg->l); + c->offset = 0; + c->flag |= COPEN; + c->mode = mode&~OTRUNC; + return c; +} + +static void +envcreate(Chan *c, char *name, int mode, ulong perm) +{ + Egrp *eg; + Evalue *e, *le; + + USED(perm); + if(c->qid.type != QTDIR) + error(Eperm); + if(strlen(name) >= sizeof(up->genbuf)) + error("name too long"); /* needs to fit for stat */ + eg = up->env->egrp; + qlock(&eg->l); + for(le = nil, e = eg->entries; e != nil; le = e, e = e->next) + if(strcmp(e->var, name) == 0) { + qunlock(&eg->l); + error(Eexist); + } + e = smalloc(sizeof(Evalue)); + e->var = smalloc(strlen(name)+1); + strcpy(e->var, name); + e->val = 0; + e->len = 0; + e->qid.path = ++eg->path; + if (le == nil) + eg->entries = e; + else + le->next = e; + e->qid.vers = 0; + c->qid = e->qid; + eg->vers++; + qunlock(&eg->l); + c->offset = 0; + c->flag |= COPEN; + c->mode = mode; +} + +static void +envclose(Chan * c) +{ + if(c->flag & CRCLOSE) + envremove(c); +} + +static long +envread(Chan *c, void *a, long n, vlong offset) +{ + Egrp *eg; + Evalue *e; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, envgen); + eg = up->env->egrp; + qlock(&eg->l); + for(e = eg->entries; e != nil; e = e->next) + if(e->qid.path == c->qid.path) + break; + if(e == nil) { + qunlock(&eg->l); + error(Enonexist); + } + if(offset > e->len) /* protects against overflow converting vlong to ulong */ + n = 0; + else if(offset + n > e->len) + n = e->len - offset; + if(n <= 0) + n = 0; + else + memmove(a, e->val+offset, n); + qunlock(&eg->l); + return n; +} + +static long +envwrite(Chan *c, void *a, long n, vlong offset) +{ + char *s; + int ve; + Egrp *eg; + Evalue *e; + + if(n <= 0) + return 0; + eg = up->env->egrp; + qlock(&eg->l); + for(e = eg->entries; e != nil; e = e->next) + if(e->qid.path == c->qid.path) + break; + if(e == nil) { + qunlock(&eg->l); + error(Enonexist); + } + ve = offset+n; + if(ve > e->len) { + s = smalloc(ve); + memmove(s, e->val, e->len); + if(e->val) + free(e->val); + e->val = s; + e->len = ve; + } + memmove(e->val+offset, a, n); + e->qid.vers++; + qunlock(&eg->l); + return n; +} + +static void +envremove(Chan *c) +{ + Egrp *eg; + Evalue *e, **l; + + if(c->qid.type & QTDIR) + error(Eperm); + eg = up->env->egrp; + qlock(&eg->l); + for(l = &eg->entries; *l != nil; l = &(*l)->next) + if((*l)->qid.path == c->qid.path) + break; + e = *l; + if(e == nil) { + qunlock(&eg->l); + error(Enonexist); + } + *l = e->next; + eg->vers++; + qunlock(&eg->l); + free(e->var); + if(e->val != nil) + free(e->val); + free(e); +} + +Dev envdevtab = { + 'e', + "env", + + devinit, + envattach, + envwalk, + envstat, + envopen, + envcreate, + envclose, + envread, + devbread, + envwrite, + devbwrite, + envremove, + devwstat +}; diff --git a/emu/port/devfs-posix.c b/emu/port/devfs-posix.c new file mode 100644 index 00000000..512c240c --- /dev/null +++ b/emu/port/devfs-posix.c @@ -0,0 +1,1058 @@ +/* + * Unix file system interface + */ +#define _LARGEFILE64_SOURCE 1 +#define _FILE_OFFSET_BITS 64 +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/fcntl.h> +#include <utime.h> +#include <dirent.h> +#include <stdio.h> +#define __EXTENSIONS__ +#undef getwd +#include <unistd.h> +#include <pwd.h> +#include <grp.h> + +typedef struct Fsinfo Fsinfo; +struct Fsinfo +{ + int uid; + int gid; + int mode; /* Unix mode */ + DIR* dir; /* open directory */ + struct dirent* de; /* directory reading */ + int fd; /* open files */ + ulong offset; /* offset when reading directory */ + QLock oq; /* mutex for offset */ + char* spec; + Cname* name; /* Unix's name for file */ + Qid rootqid; /* Plan 9's qid for Inferno's root */ +}; + +#define FS(c) ((Fsinfo*)(c)->aux) + +enum +{ + IDSHIFT = 8, + NID = 1 << IDSHIFT, + IDMASK = NID - 1, + MAXPATH = 1024 /* TO DO: eliminate this */ +}; + +typedef struct Pass Pass; +struct Pass +{ + int id; + int gid; + char* name; + Pass* next; +}; + +char rootdir[MAXROOT] = ROOT; + +static Pass* uid[NID]; +static Pass* gid[NID]; +static Pass* member[NID]; +static RWlock idl; + +static Qid fsqid(struct stat *); +static void fspath(Cname*, char*, char*); +static int fsdirconv(Chan*, char*, struct stat*, uchar*, int, int); +static Cname* fswalkpath(Cname*, char*, int); +static char* fslastelem(Cname*); +static char* id2name(Pass**, int); +static int ingroup(int id, int gid); +static void fsperm(Chan*, int); +static long fsdirread(Chan*, uchar*, int, vlong); +static int fsomode(int); +static Pass* name2pass(Pass**, char*); +static void getpwdf(void); +static void getgrpf(void); +static void fsremove(Chan*); + +/* Unix libc */ + +extern struct passwd *getpwent(void); +extern struct group *getgrent(void); + +/* + * this crud is to compensate for invalid symbolic links; + * all you can do is delete them, and good riddance + */ +static int +xstat(char *f, struct stat *sb) +{ + if(stat(f, sb) >= 0) + return 0; + /* could possibly generate ->name as rob suggested */ + return lstat(f, sb); +} + +static void +fsfree(Chan *c) +{ + cnameclose(FS(c)->name); + free(FS(c)); +} + +Chan* +fsattach(char *spec) +{ + Chan *c; + struct stat stbuf; + static int devno; + static Lock l; + + getpwdf(); + getgrpf(); + + if(!emptystr(spec) && strcmp(spec, "*") != 0) + error(Ebadspec); + if(stat(rootdir, &stbuf) < 0) + oserror(); + + c = devattach('U', spec); + c->qid = fsqid(&stbuf); + c->aux = smalloc(sizeof(Fsinfo)); + FS(c)->dir = nil; + FS(c)->de = nil; + FS(c)->fd = -1; + FS(c)->gid = stbuf.st_gid; + FS(c)->uid = stbuf.st_uid; + FS(c)->mode = stbuf.st_mode; + lock(&l); + c->dev = devno++; + unlock(&l); + if (!emptystr(spec)){ + FS(c)->spec = "/"; + FS(c)->name = newcname(FS(c)->spec); + }else + FS(c)->name = newcname(rootdir); + FS(c)->rootqid = c->qid; + + return c; +} + +Walkqid* +fswalk(Chan *c, Chan *nc, char **name, int nname) +{ + int j; + volatile int alloc; + Walkqid *wq; + struct stat stbuf; + char *n; + Cname *next; + Cname *volatile current; + Qid rootqid; + + if(nname > 0) + isdir(c); + + alloc = 0; + current = nil; + wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); + if(waserror()){ + if(alloc && wq->clone != nil) + cclose(wq->clone); + cnameclose(current); + free(wq); + return nil; + } + if(nc == nil){ + nc = devclone(c); + nc->type = 0; + alloc = 1; + } + wq->clone = nc; + rootqid = FS(c)->rootqid; + current = FS(c)->name; + if(current != nil) + incref(¤t->r); + for(j = 0; j < nname; j++){ + if(!(nc->qid.type&QTDIR)){ + if(j==0) + error(Enotdir); + break; + } + n = name[j]; + if(strcmp(n, ".") != 0 && !(isdotdot(n) && nc->qid.path == rootqid.path)){ + next = current; + incref(&next->r); + next = addelem(current, n); + //print("** ufs walk '%s' -> %s [%s]\n", current->s, n, next->s); + if(xstat(next->s, &stbuf) < 0){ + cnameclose(next); + if(j == 0) + error(Enonexist); + strcpy(up->env->errstr, Enonexist); + break; + } + nc->qid = fsqid(&stbuf); + cnameclose(current); + current = next; + } + wq->qid[wq->nqid++] = nc->qid; + } + poperror(); + if(wq->nqid < nname){ + cnameclose(current); + if(alloc) + cclose(wq->clone); + wq->clone = nil; + }else if(wq->clone){ + nc->aux = smalloc(sizeof(Fsinfo)); + nc->type = c->type; + if (nname) { + FS(nc)->gid = stbuf.st_gid; + FS(nc)->uid = stbuf.st_uid; + FS(nc)->mode = stbuf.st_mode; + } else { + FS(nc)->gid = FS(c)->gid; + FS(nc)->uid = FS(c)->uid; + FS(nc)->mode = FS(c)->mode; + } + FS(nc)->name = current; + FS(nc)->spec = FS(c)->spec; + FS(nc)->rootqid = rootqid; + FS(nc)->fd = -1; + FS(nc)->dir = nil; + FS(nc)->de = nil; + } + return wq; +} + +static int +fsstat(Chan *c, uchar *dp, int n) +{ + struct stat stbuf; + char *p; + + if(xstat(FS(c)->name->s, &stbuf) < 0) + oserror(); + p = fslastelem(FS(c)->name); + if(*p == 0) + p = "/"; + rlock(&idl); + n = fsdirconv(c, p, &stbuf, dp, n, 0); + runlock(&idl); + return n; +} + +static Chan* +fsopen(Chan *c, int mode) +{ + int m, isdir; + + m = mode & (OTRUNC|3); + switch(m) { + case 0: + fsperm(c, 4); + break; + case 1: + case 1|16: + fsperm(c, 2); + break; + case 2: + case 0|16: + case 2|16: + fsperm(c, 4); + fsperm(c, 2); + break; + case 3: + fsperm(c, 1); + break; + default: + error(Ebadarg); + } + + isdir = c->qid.type & QTDIR; + + if(isdir && mode != OREAD) + error(Eperm); + + m = fsomode(m & 3); + c->mode = openmode(mode); + + if(isdir) { + FS(c)->dir = opendir(FS(c)->name->s); + if(FS(c)->dir == 0) + oserror(); + } + else { + if(mode & OTRUNC) + m |= O_TRUNC; + FS(c)->fd = open(FS(c)->name->s, m, 0666); + if(FS(c)->fd < 0) + oserror(); + } + + c->offset = 0; + FS(c)->offset = 0; + c->flag |= COPEN; + return c; +} + +static void +fscreate(Chan *c, char *name, int mode, ulong perm) +{ + int fd, m, o; + struct stat stbuf; + Cname *n; + + fsperm(c, 2); + + m = fsomode(mode&3); + openmode(mode); /* get the errors out of the way */ + + if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) + error(Efilename); + n = fswalkpath(FS(c)->name, name, 1); + if(waserror()){ + cnameclose(n); + nexterror(); + } + if(perm & DMDIR) { + if(m) + error(Eperm); + + perm &= ~0777 | (FS(c)->mode & 0777); + if(mkdir(n->s, perm) < 0) + oserror(); + + fd = open(n->s, 0); + if(fd < 0) + oserror(); + fchmod(fd, perm); + fchown(fd, up->env->uid, FS(c)->gid); + if(fstat(fd, &stbuf) <0){ + close(fd); + oserror(); + } + close(fd); + FS(c)->dir = opendir(n->s); + if(FS(c)->dir == nil) + oserror(); + } else { + o = (O_CREAT | O_EXCL) | (mode&3); + if(mode & OTRUNC) + o |= O_TRUNC; + perm &= ~0666 | (FS(c)->mode & 0666); + fd = open(n->s, o, perm); + if(fd < 0) + oserror(); + fchmod(fd, perm); + fchown(fd, up->env->uid, FS(c)->gid); + if(fstat(fd, &stbuf) < 0){ + close(fd); + oserror(); + } + FS(c)->fd = fd; + } + cnameclose(FS(c)->name); + FS(c)->name = n; + poperror(); + + c->qid = fsqid(&stbuf); + FS(c)->gid = stbuf.st_gid; + FS(c)->uid = stbuf.st_uid; + FS(c)->mode = stbuf.st_mode; + c->mode = openmode(mode); + c->offset = 0; + FS(c)->offset = 0; + c->flag |= COPEN; +} + +static void +fsclose(Chan *c) +{ + if((c->flag & COPEN) != 0){ + if(c->qid.type & QTDIR) + closedir(FS(c)->dir); + else + close(FS(c)->fd); + } + if(c->flag & CRCLOSE) { + if(!waserror()) { + fsremove(c); + poperror(); + } + return; + } + fsfree(c); +} + +static long +fsread(Chan *c, void *va, long n, vlong offset) +{ + long r; + + if(c->qid.type & QTDIR){ + qlock(&FS(c)->oq); + if(waserror()) { + qunlock(&FS(c)->oq); + nexterror(); + } + r = fsdirread(c, va, n, offset); + poperror(); + qunlock(&FS(c)->oq); + }else{ + r = pread(FS(c)->fd, va, n, offset); + if(r < 0 && (errno == ESPIPE || errno == EPIPE)){ + r = read(FS(c)->fd, va, n); + if(r < 0) + oserror(); + } + } + return r; +} + +static long +fswrite(Chan *c, void *va, long n, vlong offset) +{ + long r; + + r = pwrite(FS(c)->fd, va, n, offset); + if(r < 0 && (errno == ESPIPE || errno == EPIPE)){ + r = write(FS(c)->fd, va, n); + if(r < 0) + oserror(); + } + return r; +} + +static void +fswchk(Cname *c) +{ + struct stat stbuf; + + if(stat(c->s, &stbuf) < 0) + oserror(); + + if(stbuf.st_uid == up->env->uid) + stbuf.st_mode >>= 6; + else + if(stbuf.st_gid == up->env->gid || ingroup(up->env->uid, stbuf.st_gid)) + stbuf.st_mode >>= 3; + + if(stbuf.st_mode & S_IWOTH) + return; + + error(Eperm); +} + +static void +fsremove(Chan *c) +{ + int n; + volatile struct { Cname *dir; } dir; + + dir.dir = fswalkpath(FS(c)->name, "..", 1); + if(waserror()){ + if(dir.dir != nil) + cnameclose(dir.dir); + fsfree(c); + nexterror(); + } + fswchk(dir.dir); + cnameclose(dir.dir); + dir.dir = nil; + if(c->qid.type & QTDIR) + n = rmdir(FS(c)->name->s); + else + n = remove(FS(c)->name->s); + if(n < 0) + oserror(); + poperror(); + fsfree(c); +} + +static int +fswstat(Chan *c, uchar *buf, int nb) +{ + Dir *d; + Pass *p; + volatile struct { Cname *ph; } ph; + struct stat stbuf; + struct utimbuf utbuf; + int tsync; + + if(FS(c)->fd >= 0){ + if(fstat(FS(c)->fd, &stbuf) < 0) + oserror(); + }else{ + if(stat(FS(c)->name->s, &stbuf) < 0) + oserror(); + } + d = malloc(sizeof(*d)+nb); + if(d == nil) + error(Enomem); + if(waserror()){ + free(d); + nexterror(); + } + tsync = 1; + nb = convM2D(buf, nb, d, (char*)&d[1]); + if(nb == 0) + error(Eshortstat); + if(!emptystr(d->name) && strcmp(d->name, fslastelem(FS(c)->name)) != 0) { + tsync = 0; + validname(d->name, 0); + ph.ph = fswalkpath(FS(c)->name, "..", 1); + if(waserror()){ + cnameclose(ph.ph); + nexterror(); + } + fswchk(ph.ph); + ph.ph = fswalkpath(ph.ph, d->name, 0); + if(rename(FS(c)->name->s, ph.ph->s) < 0) + oserror(); + cnameclose(FS(c)->name); + poperror(); + FS(c)->name = ph.ph; + } + + if(d->mode != ~0 && (d->mode&0777) != (stbuf.st_mode&0777)) { + tsync = 0; + if(up->env->uid != stbuf.st_uid) + error(Eowner); + if(FS(c)->fd >= 0){ + if(fchmod(FS(c)->fd, d->mode&0777) < 0) + oserror(); + }else{ + if(chmod(FS(c)->name->s, d->mode&0777) < 0) + oserror(); + } + FS(c)->mode &= ~0777; + FS(c)->mode |= d->mode&0777; + } + + if(d->atime != ~0 && d->atime != stbuf.st_atime + || d->mtime != ~0 && d->mtime != stbuf.st_mtime) { + tsync = 0; + if(up->env->uid != stbuf.st_uid) + error(Eowner); + if(d->mtime != ~0) + utbuf.modtime = d->mtime; + else + utbuf.modtime = stbuf.st_mtime; + if(d->atime != ~0) + utbuf.actime = d->atime; + else + utbuf.actime = stbuf.st_atime; + if(utime(FS(c)->name->s, &utbuf) < 0) /* TO DO: futimes isn't portable */ + oserror(); + } + + if(*d->gid){ + tsync = 0; + rlock(&idl); + if(waserror()){ + runlock(&idl); + nexterror(); + } + p = name2pass(gid, d->gid); + if(p == 0) + error(Eunknown); + if(p->id != stbuf.st_gid) { + if(up->env->uid != stbuf.st_uid) + error(Eowner); + if(FS(c)->fd >= 0){ + if(fchown(FS(c)->fd, stbuf.st_uid, p->id) < 0) + oserror(); + }else{ + if(chown(FS(c)->name->s, stbuf.st_uid, p->id) < 0) + oserror(); + } + FS(c)->gid = p->id; + } + poperror(); + runlock(&idl); + } + + if(d->length != ~(uvlong)0){ + tsync = 0; + if(FS(c)->fd >= 0){ + fsperm(c, 2); + if(ftruncate(FS(c)->fd, d->length) < 0) + oserror(); + }else{ + fswchk(FS(c)->name); + if(truncate(FS(c)->name->s, d->length) < 0) + oserror(); + } + } + + poperror(); + free(d); + if(tsync && FS(c)->fd >= 0 && fsync(FS(c)->fd) < 0) + oserror(); + return nb; +} + +#define QDEVBITS 4 /* 16 devices should be plenty */ +#define MAXDEV (1<<QDEVBITS) +#define QDEVSHIFT (64-QDEVBITS) +#define QINOMASK (((uvlong)1<<QDEVSHIFT)-1) +#define QPATH(d,i) (((uvlong)(d)<<QDEVSHIFT)|((uvlong)(i)&QINOMASK)) + +static Qid +fsqid(struct stat *st) +{ + Qid q; + ulong dev; + int idev; + static int nqdev = 0; + static ulong qdev[MAXDEV]; + static Lock l; + + q.type = QTFILE; + if(S_ISDIR(st->st_mode)) + q.type = QTDIR; + + dev = st->st_dev; + lock(&l); + for(idev = 0; idev < nqdev; idev++) + if(qdev[idev] == dev) + break; + if(idev == nqdev) { + if(nqdev == MAXDEV) { + unlock(&l); + error("too many devices"); + } + qdev[nqdev++] = dev; + } + unlock(&l); + + if(0) /* we'll just let it be masked off */ + if((uvlong)st->st_ino & ~QINOMASK) + error("inode number too large"); + + q.path = QPATH(idev, st->st_ino); + q.vers = st->st_mtime; + + return q; +} + +static void +fspath(Cname *c, char *name, char *path) +{ + int n; + + if(c->len+strlen(name) >= MAXPATH) + panic("fspath: name too long"); + memmove(path, c->s, c->len); + n = c->len; + if(path[n-1] != '/') + path[n++] = '/'; + strcpy(path+n, name); + if(isdotdot(name)) + cleanname(path); +/*print("->%s\n", path);*/ +} + +static Cname * +fswalkpath(Cname *c, char *name, int dup) +{ + if(dup) + c = newcname(c->s); + c = addelem(c, name); + if(isdotdot(name)) + cleancname(c); + return c; +} + +static char * +fslastelem(Cname *c) +{ + char *p; + + p = c->s + c->len; + while(p > c->s && p[-1] != '/') + p--; + return p; +} + +/* + * Assuming pass is one of the static arrays protected by idl, caller must + * hold idl in writer mode. + */ +static void +freepass(Pass **pass) +{ + int i; + Pass *p, *np; + + for(i=0; i<NID; i++){ + for(p = pass[i]; p; p = np){ + np = p->next; + free(p); + } + pass[i] = 0; + } +} + +static void +getpwdf(void) +{ + unsigned i; + Pass *p; + static int mtime; /* serialized by idl */ + struct stat stbuf; + struct passwd *pw; + + if(stat("/etc/passwd", &stbuf) < 0) + panic("can't read /etc/passwd"); + + /* + * Unlocked peek is okay, since the check is a heuristic (as is + * the function). + */ + if(stbuf.st_mtime <= mtime) + return; + + wlock(&idl); + if(stbuf.st_mtime <= mtime) { + /* + * If we lost a race on updating the database, we can + * avoid some work. + */ + wunlock(&idl); + return; + } + mtime = stbuf.st_mtime; + freepass(uid); + setpwent(); + while(pw = getpwent()){ + i = pw->pw_uid; + i = (i&IDMASK) ^ ((i>>IDSHIFT)&IDMASK); + p = realloc(0, sizeof(Pass)); + if(p == 0) + panic("getpwdf"); + + p->next = uid[i]; + uid[i] = p; + p->id = pw->pw_uid; + p->gid = pw->pw_gid; + p->name = strdup(pw->pw_name); + if(p->name == 0) + panic("no memory"); + } + + wunlock(&idl); + endpwent(); +} + +static void +getgrpf(void) +{ + static int mtime; /* serialized by idl */ + struct stat stbuf; + struct group *pw; + unsigned i; + int j; + Pass *p, *q; + + if(stat("/etc/group", &stbuf) < 0) + panic("can't read /etc/group"); + + /* + * Unlocked peek is okay, since the check is a heuristic (as is + * the function). + */ + if(stbuf.st_mtime <= mtime) + return; + + wlock(&idl); + if(stbuf.st_mtime <= mtime) { + /* + * If we lost a race on updating the database, we can + * avoid some work. + */ + wunlock(&idl); + return; + } + mtime = stbuf.st_mtime; + freepass(gid); + freepass(member); + /* + * Pass one -- group name to gid mapping. + */ + setgrent(); + while(pw = getgrent()){ + i = pw->gr_gid; + i = (i&IDMASK) ^ ((i>>IDSHIFT)&IDMASK); + p = realloc(0, sizeof(Pass)); + if(p == 0) + panic("getpwdf"); + p->next = gid[i]; + gid[i] = p; + p->id = pw->gr_gid; + p->gid = 0; + p->name = strdup(pw->gr_name); + if(p->name == 0) + panic("no memory"); + } + /* + * Pass two -- group memberships. + */ + setgrent(); + while(pw = getgrent()){ + for (j = 0;; j++) { + if (pw->gr_mem[j] == nil) + break; + q = name2pass(gid, pw->gr_mem[j]); + if (q == nil) + continue; + i = q->id + pw->gr_gid; + i = (i&IDMASK) ^ ((i>>IDSHIFT)&IDMASK); + p = realloc(0, sizeof(Pass)); + if(p == 0) + panic("getpwdf"); + p->next = member[i]; + member[i] = p; + p->id = q->id; + p->gid = pw->gr_gid; + } + } + + wunlock(&idl); + endgrent(); +} + +/* Caller must hold idl. Does not raise an error. */ +static Pass* +name2pass(Pass **pw, char *name) +{ + int i; + static Pass *p; + static Pass **pwdb; + + if(p && pwdb == pw && strcmp(name, p->name) == 0) + return p; + + for(i=0; i<NID; i++) + for(p = pw[i]; p; p = p->next) + if(strcmp(name, p->name) == 0) { + pwdb = pw; + return p; + } + + return 0; +} + +/* Caller must hold idl. Does not raise an error. */ +static char* +id2name(Pass **pw, int id) +{ + int i; + Pass *p; + char *s; + + s = nil; + /* use last on list == first in file */ + i = (id&IDMASK) ^ ((id>>IDSHIFT)&IDMASK); + for(p = pw[i]; p; p = p->next) + if(p->id == id) + s = p->name; + if(s) + return s; + return ""; /* TO DO: should be "%d" */ +} + +/* Caller must hold idl. Does not raise an error. */ +static int +ingroup(int id, int gid) +{ + int i; + Pass *p; + + i = id+gid; + i = (id&IDMASK) ^ ((id>>IDSHIFT)&IDMASK); + for(p = member[i]; p; p = p->next) + if(p->id == id && p->gid == gid) + return 1; + return 0; +} + +static void +fsperm(Chan *c, int mask) +{ + int m; + + m = FS(c)->mode; +/* + print("fsperm: %o %o uuid %d ugid %d cuid %d cgid %d\n", + m, mask, up->env->uid, up->env->gid, FS(c)->uid, FS(c)->gid); +*/ + if(FS(c)->uid == up->env->uid) + m >>= 6; + else + if(FS(c)->gid == up->env->gid || ingroup(up->env->uid, FS(c)->gid)) + m >>= 3; + + m &= mask; + if(m == 0) + error(Eperm); +} + +static int +isdots(char *name) +{ + return name[0] == '.' && (name[1] == '\0' || name[1] == '.' && name[2] == '\0'); +} + +static int +fsdirconv(Chan *c, char *name, struct stat *s, uchar *va, int nb, int indir) +{ + Dir d; + + memset(&d, 0, sizeof(d)); + d.name = name; + d.uid = id2name(uid, s->st_uid); + d.gid = id2name(gid, s->st_gid); + d.muid = ""; + d.qid = fsqid(s); + d.mode = (d.qid.type<<24)|(s->st_mode&0777); + d.atime = s->st_atime; + d.mtime = s->st_mtime; + d.length = s->st_size; + if(d.mode&DMDIR) + d.length = 0; + d.type = 'U'; + d.dev = c->dev; + if(indir && sizeD2M(&d) > nb) + return -1; /* directory reader needs to know it didn't fit */ + return convD2M(&d, va, nb); +} + +static long +fsdirread(Chan *c, uchar *va, int count, vlong offset) +{ + int i; + long n, r; + struct stat stbuf; + char path[MAXPATH], *ep; + struct dirent *de; + static char slop[8192]; + + i = 0; + fspath(FS(c)->name, "", path); + ep = path+strlen(path); + if(FS(c)->offset != offset) { + seekdir(FS(c)->dir, 0); + FS(c)->de = nil; + for(n=0; n<offset; ) { + de = readdir(FS(c)->dir); + if(de == 0) { + /* EOF, so stash offset and return 0 */ + FS(c)->offset = n; + return 0; + } + if(de->d_ino==0 || de->d_name[0]==0 || isdots(de->d_name)) + continue; + strecpy(ep, path+sizeof(path), de->d_name); + if(xstat(path, &stbuf) < 0) { + fprint(2, "dir: bad path %s\n", path); + continue; + } + rlock(&idl); + r = fsdirconv(c, de->d_name, &stbuf, slop, sizeof(slop), 1); + runlock(&idl); + if(r <= 0) { + FS(c)->offset = n; + return 0; + } + n += r; + } + FS(c)->offset = offset; + } + + /* + * Take idl on behalf of id2name. Stalling attach, which is a + * rare operation, until the readdir completes is probably + * preferable to adding lock round-trips. + */ + rlock(&idl); + while(i < count){ + de = FS(c)->de; + FS(c)->de = nil; + if(de == nil) + de = readdir(FS(c)->dir); + if(de == nil) + break; + + if(de->d_ino==0 || de->d_name[0]==0 || isdots(de->d_name)) + continue; + + strecpy(ep, path+sizeof(path), de->d_name); + if(xstat(path, &stbuf) < 0) { + fprint(2, "dir: bad path %s\n", path); + continue; + } + r = fsdirconv(c, de->d_name, &stbuf, va+i, count-i, 1); + if(r <= 0){ + FS(c)->de = de; + break; + } + i += r; + FS(c)->offset += r; + } + runlock(&idl); + return i; +} + +static int +fsomode(int m) +{ + if(m < 0 || m > 3) + error(Ebadarg); + return m == 3? 0: m; +} + +void +setid(char *name, int owner) +{ + Pass *p; + + if(owner && !iseve()) + return; + kstrdup(&up->env->user, name); + + rlock(&idl); + p = name2pass(uid, name); + if(p == nil){ + runlock(&idl); + up->env->uid = -1; + up->env->gid = -1; + return; + } + + up->env->uid = p->id; + up->env->gid = p->gid; + runlock(&idl); +} + +Dev fsdevtab = { + 'U', + "fs", + + devinit, + fsattach, + fswalk, + fsstat, + fsopen, + fscreate, + fsclose, + fsread, + devbread, + fswrite, + devbwrite, + fsremove, + fswstat +}; diff --git a/emu/port/devindir.c b/emu/port/devindir.c new file mode 100644 index 00000000..9c214927 --- /dev/null +++ b/emu/port/devindir.c @@ -0,0 +1,35 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +static Chan * +indirattach(char *spec) +{ + char *p; + Dev *d; + + if(*spec == 0) + error(Ebadspec); + p = strrchr(spec, '!'); + if(p == nil) + p = ""; + else + *p++ = 0; + d = devbyname(spec); + if(d == nil || d->dc == '*'){ + snprint(up->env->errstr, ERRMAX, "unknown device: %s", spec); + error(up->env->errstr); + } + if(up->env->pgrp->nodevs && + (utfrune("|esDa", d->dc) == nil || d->dc == 's' && *p!='\0')) + error(Enoattach); + return d->attach(p); +} + +Dev indirdevtab = { + '*', + "indir", + + devinit, + indirattach, +}; diff --git a/emu/port/devip.c b/emu/port/devip.c new file mode 100644 index 00000000..8572a184 --- /dev/null +++ b/emu/port/devip.c @@ -0,0 +1,1133 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "ip.h" + +enum +{ + Qtopdir = 1, /* top level directory */ + Qtopbase, + Qarp= Qtopbase, +/* Qiproute, */ +/* Qipselftab, */ + Qndb, + + Qprotodir, /* directory for a protocol */ + Qprotobase, + Qclone= Qprotobase, + Qstats, + + Qconvdir, /* directory for a conversation */ + Qconvbase, + Qctl= Qconvbase, + Qdata, + Qlisten, + Qlocal, + Qremote, + Qstatus, + + Logtype= 5, + Masktype= (1<<Logtype)-1, + Logconv= 12, + Maskconv= (1<<Logconv)-1, + Shiftconv= Logtype, + Logproto= 8, + Maskproto= (1<<Logproto)-1, + Shiftproto= Logtype + Logconv, + + Statelen = 256, + + Nfs= 1, + + Maxproto = 4, + MAXCONV = 4096 +}; +#define TYPE(x) ( ((ulong)(x).path) & Masktype ) +#define CONV(x) ( (((ulong)(x).path) >> Shiftconv) & Maskconv ) +#define PROTO(x) ( (((ulong)(x).path) >> Shiftproto) & Maskproto ) +#define QID(p, c, y) ( ((p)<<(Shiftproto)) | ((c)<<Shiftconv) | (y) ) + +enum +{ + Idle= 0, + Announcing= 1, + Announced= 2, + Connecting= 3, + Connected= 4, + Hungup= 5, +}; + +struct Conv +{ + QLock l; + + int x; /* conversation index */ + Proto* p; + + uchar laddr[IPaddrlen]; /* local IP address */ + uchar raddr[IPaddrlen]; /* remote IP address */ + int restricted; /* remote port is restricted */ + ushort lport; /* local port number */ + ushort rport; /* remote port number */ + + char* owner; /* protections */ + int perm; + int inuse; /* opens of listen/data/ctl */ + int state; + + /* udp specific */ + int headers; /* data src/dst headers in udp */ + + char cerr[ERRMAX]; + + QLock listenq; + + void* ptcl; /* protocol specific stuff */ + + int sfd; + QLock wlock; /* prevent data from being split by concurrent writes */ +}; + +struct Proto +{ + QLock l; + int x; + int ipproto; + int stype; + char* name; + int maxconv; + Fs* f; /* file system this proto is part of */ + Conv** conv; /* array of conversations */ + int pctlsize; /* size of per protocol ctl block */ + int nc; /* number of conversations */ + int ac; + Qid qid; /* qid for protocol directory */ + /* port allocation isn't done here when hosted */ + + void* priv; +}; + +/* + * one per IP protocol stack + */ +struct Fs +{ + RWlock l; + int dev; + + int np; + Proto* p[Maxproto+1]; /* list of supported protocols */ + Proto* t2p[256]; /* vector of all protocols */ + + char ndb[1024]; /* an ndb entry for this interface */ + int ndbvers; + long ndbmtime; +}; + +static Fs *ipfs[Nfs]; /* attached fs's */ +static char network[] = "network"; +static char* ipstates[] = { + "Closed", /* Idle */ + "Announcing", + "Announced", + "Connecting", + "Established", /* Connected */ + "Closed", /* Hungup */ +}; + +static Conv* protoclone(Proto*, char*, int); +static Conv* newconv(Proto*, Conv **); +static void setladdr(Conv*); +static ulong ip6w(uchar*); +static void ipw6(uchar*, ulong); + +static int +ip3gen(Chan *c, int i, Dir *dp) +{ + Qid q; + Conv *cv; + char *p; + + cv = ipfs[c->dev]->p[PROTO(c->qid)]->conv[CONV(c->qid)]; + if(cv->owner == nil) + kstrdup(&cv->owner, eve); + mkqid(&q, QID(PROTO(c->qid), CONV(c->qid), i), 0, QTFILE); + + switch(i) { + default: + return -1; + case Qctl: + devdir(c, q, "ctl", 0, cv->owner, cv->perm, dp); + return 1; + case Qdata: + devdir(c, q, "data", 0, cv->owner, cv->perm, dp); + return 1; + case Qlisten: + devdir(c, q, "listen", 0, cv->owner, cv->perm, dp); + return 1; + case Qlocal: + p = "local"; + break; + case Qremote: + p = "remote"; + break; + case Qstatus: + p = "status"; + break; + } + devdir(c, q, p, 0, cv->owner, 0444, dp); + return 1; +} + +static int +ip2gen(Chan *c, int i, Dir *dp) +{ + Qid q; + + switch(i) { + case Qclone: + mkqid(&q, QID(PROTO(c->qid), 0, Qclone), 0, QTFILE); + devdir(c, q, "clone", 0, network, 0666, dp); + return 1; + case Qstats: + mkqid(&q, QID(PROTO(c->qid), 0, Qstats), 0, QTFILE); + devdir(c, q, "stats", 0, network, 0444, dp); + return 1; + } + return -1; +} + +static int +ip1gen(Chan *c, int i, Dir *dp) +{ + Qid q; + char *p; + int prot; + int len = 0; + Fs *f; + extern ulong kerndate; + + f = ipfs[c->dev]; + + prot = 0664; + mkqid(&q, QID(0, 0, i), 0, QTFILE); + switch(i) { + default: + return -1; + case Qarp: + p = "arp"; + break; + case Qndb: + p = "ndb"; + len = strlen(ipfs[c->dev]->ndb); + break; +/* case Qiproute: + p = "iproute"; + break; + case Qipselftab: + p = "ipselftab"; + prot = 0444; + break; + case Qiprouter: + p = "iprouter"; + break; + case Qlog: + p = "log"; + break; +*/ + } + devdir(c, q, p, len, network, prot, dp); + if(i == Qndb && f->ndbmtime > kerndate) + dp->mtime = f->ndbmtime; + return 1; +} + +static int +ipgen(Chan *c, char *name, Dirtab *tab, int x, int s, Dir *dp) +{ + Qid q; + Conv *cv; + Fs *f; + + USED(name); + USED(tab); + USED(x); + f = ipfs[c->dev]; + + switch(TYPE(c->qid)) { + case Qtopdir: + if(s == DEVDOTDOT){ + mkqid(&q, QID(0, 0, Qtopdir), 0, QTDIR); + sprint(up->genbuf, "#I%lud", c->dev); + devdir(c, q, up->genbuf, 0, network, 0555, dp); + return 1; + } + if(s < f->np) { +/* if(f->p[s]->connect == nil) + return 0; /* protocol with no user interface */ + mkqid(&q, QID(s, 0, Qprotodir), 0, QTDIR); + devdir(c, q, f->p[s]->name, 0, network, 0555, dp); + return 1; + } + s -= f->np; + return ip1gen(c, s+Qtopbase, dp); + case Qarp: + case Qndb: +/* case Qiproute: + case Qiprouter: + case Qipselftab: */ + return ip1gen(c, TYPE(c->qid), dp); + case Qprotodir: + if(s == DEVDOTDOT){ + mkqid(&q, QID(0, 0, Qtopdir), 0, QTDIR); + sprint(up->genbuf, "#I%lud", c->dev); + devdir(c, q, up->genbuf, 0, network, 0555, dp); + return 1; + } + if(s < f->p[PROTO(c->qid)]->ac) { + cv = f->p[PROTO(c->qid)]->conv[s]; + sprint(up->genbuf, "%d", s); + mkqid(&q, QID(PROTO(c->qid), s, Qconvdir), 0, QTDIR); + devdir(c, q, up->genbuf, 0, cv->owner, 0555, dp); + return 1; + } + s -= f->p[PROTO(c->qid)]->ac; + return ip2gen(c, s+Qprotobase, dp); + case Qclone: + case Qstats: + return ip2gen(c, TYPE(c->qid), dp); + case Qconvdir: + if(s == DEVDOTDOT){ + s = PROTO(c->qid); + mkqid(&q, QID(s, 0, Qprotodir), 0, QTDIR); + devdir(c, q, f->p[s]->name, 0, network, 0555, dp); + return 1; + } + return ip3gen(c, s+Qconvbase, dp); + case Qctl: + case Qdata: + case Qlisten: + case Qlocal: + case Qremote: + case Qstatus: + return ip3gen(c, TYPE(c->qid), dp); + } + return -1; +} + +static void +newproto(char *name, int type, int maxconv) +{ + Proto *p; + + p = smalloc(sizeof(*p)); + p->name = name; + p->stype = type; + p->ipproto = type+1; /* temporary */ + p->nc = maxconv; + if(Fsproto(ipfs[0], p)) + panic("can't newproto %s", name); +} + +void +ipinit(void) +{ + ipfs[0] = malloc(sizeof(Fs)); + if(ipfs[0] == nil) + panic("no memory for IP stack"); + + newproto("udp", S_UDP, 64); + newproto("tcp", S_TCP, 2048); + + fmtinstall('i', eipfmt); + fmtinstall('I', eipfmt); + fmtinstall('E', eipfmt); + fmtinstall('V', eipfmt); + fmtinstall('M', eipfmt); +} + +Chan * +ipattach(char *spec) +{ + Chan *c; + + if(atoi(spec) != 0) + error("bad specification"); + + c = devattach('I', spec); + mkqid(&c->qid, QID(0, 0, Qtopdir), 0, QTDIR); + c->dev = 0; + + return c; +} + +static Walkqid* +ipwalk(Chan* c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, ipgen); +} + +static int +ipstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, ipgen); +} + +static int m2p[] = { + 4, + 2, + 6, +}; + +static Chan * +ipopen(Chan *c, int omode) +{ + Conv *cv, *nc; + Proto *p; + ulong raddr; + ushort rport; + int perm, sfd; + Fs *f; + + perm = m2p[omode&3]; + + f = ipfs[c->dev]; + + switch(TYPE(c->qid)) { + default: + break; + case Qtopdir: + case Qprotodir: + case Qconvdir: + case Qstatus: + case Qremote: + case Qlocal: + case Qstats: + /* case Qipselftab: */ + if(omode != OREAD) + error(Eperm); + break; + case Qndb: + if(omode & (OWRITE|OTRUNC) && !iseve()) + error(Eperm); + if((omode & (OWRITE|OTRUNC)) == (OWRITE|OTRUNC)){ + f->ndb[0] = 0; + f->ndbvers++; + } + break; + case Qclone: + p = f->p[PROTO(c->qid)]; + cv = protoclone(p, up->env->user, -1); + if(cv == 0) + error(Enodev); + mkqid(&c->qid, QID(p->x, cv->x, Qctl), 0, QTFILE); + break; + case Qdata: + case Qctl: + p = f->p[PROTO(c->qid)]; + qlock(&p->l); + cv = p->conv[CONV(c->qid)]; + qlock(&cv->l); + if(waserror()){ + qunlock(&cv->l); + qunlock(&p->l); + nexterror(); + } + if((perm & (cv->perm>>6)) != perm) { + if(strcmp(up->env->user, cv->owner) != 0) + error(Eperm); + if((perm & cv->perm) != perm) + error(Eperm); + } + cv->inuse++; + if(cv->inuse == 1) { + kstrdup(&cv->owner, up->env->user); + cv->perm = 0660; + } + poperror(); + qunlock(&cv->l); + qunlock(&p->l); + break; + case Qlisten: + p = f->p[PROTO(c->qid)]; + cv = p->conv[CONV(c->qid)]; + if((perm & (cv->perm>>6)) != perm){ + if(strcmp(up->env->user, cv->owner) != 0) + error(Eperm); + if((perm & cv->perm) != perm) + error(Eperm); + } + + if(cv->state != Announced) + error("not announced"); + + qlock(&cv->listenq); + if(waserror()){ + qunlock(&cv->listenq); + nexterror(); + } + + sfd = so_accept(cv->sfd, &raddr, &rport); + + nc = protoclone(p, up->env->user, sfd); + if(nc == 0) { + so_close(sfd); + error(Enodev); + } + ipw6(nc->raddr, raddr); + nc->rport = rport; + setladdr(nc); + nc->state = Connected; + mkqid(&c->qid, QID(PROTO(c->qid), nc->x, Qctl), 0, QTFILE); + + poperror(); + qunlock(&cv->listenq); + break; + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +closeconv(Conv *cv) +{ + int fd; + + qlock(&cv->l); + + if(--cv->inuse > 0) { + qunlock(&cv->l); + return; + } + + if(waserror()){ + qunlock(&cv->l); + return; + } + kstrdup(&cv->owner, network); + cv->perm = 0660; + /* cv->p->close(cv); */ + cv->state = Idle; + fd = cv->sfd; + cv->sfd = -1; + if(fd >= 0) + so_close(fd); + poperror(); + qunlock(&cv->l); +} + +static void +ipclose(Chan *c) +{ + Fs *f; + + f = ipfs[c->dev]; + switch(TYPE(c->qid)) { + case Qdata: + case Qctl: + if(c->flag & COPEN) + closeconv(f->p[PROTO(c->qid)]->conv[CONV(c->qid)]); + break; + } +} + +static long +ipread(Chan *ch, void *a, long n, vlong off) +{ + int r; + Conv *c; + Proto *x; + char *p, *s; + Fs *f; + ulong offset = off; + + f = ipfs[ch->dev]; + + p = a; + switch(TYPE(ch->qid)) { + default: + error(Eperm); + case Qprotodir: + case Qtopdir: + case Qconvdir: + return devdirread(ch, a, n, 0, 0, ipgen); + case Qarp: + error(Eperm); /* TO DO */ + case Qndb: + return readstr(off, a, n, f->ndb); + case Qctl: + sprint(up->genbuf, "%lud", CONV(ch->qid)); + return readstr(offset, p, n, up->genbuf); + case Qremote: + x = f->p[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + sprint(up->genbuf, "%I!%d\n", c->raddr, c->rport); + return readstr(offset, p, n, up->genbuf); + case Qlocal: + x = f->p[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + sprint(up->genbuf, "%I!%d\n", c->laddr, c->lport); + return readstr(offset, p, n, up->genbuf); + case Qstatus: + x = f->p[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + s = smalloc(Statelen); + if(waserror()){ + free(s); + nexterror(); + } + snprint(s, Statelen, "%s\n", ipstates[c->state]); + n = readstr(offset, p, n, s); + poperror(); + free(s); + return n; + case Qdata: + x = f->p[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + if(c->sfd < 0) + error(Ehungup); + if(c->headers) { + if(n < c->headers) + error(Ebadarg); + p = a; + r = so_recv(c->sfd, p + c->headers, n - c->headers, p, c->headers); + if(r > 0) + r += c->headers; + } else + r = so_recv(c->sfd, a, n, nil, 0); + if(r < 0) + oserror(); + return r; + case Qstats: + error("stats not implemented"); + return n; + } +} + +static void +setladdr(Conv *c) +{ + ulong laddr; + + /* TO DO: this can't be right for hosts with several addresses */ + so_getsockname(c->sfd, &laddr, &c->lport); + ipw6(c->laddr, laddr); +} + +/* + * pick a local port and set it + */ +static void +setlport(Conv *c) +{ + ulong laddr; + + so_bind(c->sfd, c->restricted, c->lport); + if(c->lport == 0) + so_getsockname(c->sfd, &laddr, &c->lport); +} + +static int +portno(char *p) +{ + long n; + char *e; + + n = strtoul(p, &e, 0); + if(p == e) + error("non-numeric port number"); + return n; +} + +/* + * set a local address and port from a string of the form + * [address!]port[!r] + */ +static void +setladdrport(Conv *c, char *str, int announcing) +{ + char *p; + int lport; + + /* + * ignore restricted part if it exists. it's + * meaningless on local ports. + */ + p = strchr(str, '!'); + if(p != nil){ + *p++ = 0; + if(strcmp(p, "r") == 0) + p = nil; + } + + c->lport = 0; + if(p == nil){ + if(announcing) + ipmove(c->laddr, IPnoaddr); + else if(0) + setladdr(c); + p = str; + } else { + if(strcmp(str, "*") == 0) + ipmove(c->laddr, IPnoaddr); + else + parseip(c->laddr, str); + } + + if(announcing && strcmp(p, "*") == 0){ + if(!iseve()) + error(Eperm); + c->lport = 0; + setlport(c); + return; + } + + lport = portno(p); + if(lport <= 0) + c->lport = 0; + else + c->lport = lport; + + setlport(c); +} + +static char* +setraddrport(Conv *c, char *str) +{ + char *p; + + p = strchr(str, '!'); + if(p == nil) + return "malformed address"; + *p++ = 0; + parseip(c->raddr, str); + c->rport = portno(p); + p = strchr(p, '!'); + if(p){ + if(strstr(p, "!r") != nil) + c->restricted = 1; + } + return nil; +} + +static void +connectctlmsg(Proto *x, Conv *c, Cmdbuf *cb) +{ + char *p; + + USED(x); + if(c->state != Idle) + error(Econinuse); + c->state = Connecting; + c->cerr[0] = '\0'; + switch(cb->nf) { + default: + error("bad args to connect"); + case 2: + p = setraddrport(c, cb->f[1]); + if(p != nil) + error(p); + break; + case 3: + p = setraddrport(c, cb->f[1]); + if(p != nil) + error(p); + c->lport = portno(cb->f[2]); + setlport(c); + break; + } + qunlock(&c->l); + if(waserror()){ + qlock(&c->l); + c->state = Connected; /* sic */ + nexterror(); + } + /* p = x->connect(c, cb->f, cb->nf); */ + so_connect(c->sfd, ip6w(c->raddr), c->rport); + qlock(&c->l); + poperror(); + setladdr(c); + c->state = Connected; +} + +static void +announcectlmsg(Proto *x, Conv *c, Cmdbuf *cb) +{ + if(c->state != Idle) + error(Econinuse); + c->state = Announcing; + c->cerr[0] = '\0'; + ipmove(c->raddr, IPnoaddr); + c->rport = 0; + switch(cb->nf){ + default: + error("bad args to announce"); + case 2: + setladdrport(c, cb->f[1], 1); + break; + } + USED(x); + /* p = x->announce(c, cb->f, cb->nf); */ + if(c->p->stype != S_UDP){ + qunlock(&c->l); + if(waserror()){ + c->state = Announced; /* sic */ + qlock(&c->l); + nexterror(); + } + so_listen(c->sfd); + qlock(&c->l); + poperror(); + } + c->state = Announced; +} + +static void +bindctlmsg(Proto *x, Conv *c, Cmdbuf *cb) +{ + USED(x); + switch(cb->nf){ + default: + error("bad args to bind"); + case 2: + setladdrport(c, cb->f[1], 0); + break; + } +} + +static long +ipwrite(Chan *ch, void *a, long n, vlong off) +{ + Conv *c; + Proto *x; + char *p; + Cmdbuf *cb; + Fs *f; + + f = ipfs[ch->dev]; + + switch(TYPE(ch->qid)) { + default: + error(Eperm); + case Qdata: + x = f->p[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + if(c->sfd < 0) + error(Ehungup); + qlock(&c->wlock); + if(waserror()){ + qunlock(&c->wlock); + nexterror(); + } + if(c->headers) { + if(n < c->headers) + error(Eshort); + p = a; + n = so_send(c->sfd, p + c->headers, n - c->headers, p, c->headers); + if(n >= 0) + n += c->headers; + } else + n = so_send(c->sfd, a, n, nil, 0); + poperror(); + qunlock(&c->wlock); + if(n < 0) + oserror(); + break; + case Qarp: + return arpwrite(a, n); + case Qndb: + if(off > strlen(f->ndb)) + error(Eio); + if(off+n >= sizeof(f->ndb)-1) + error(Eio); + memmove(f->ndb+off, a, n); + f->ndb[off+n] = 0; + f->ndbvers++; + f->ndbmtime = seconds(); + break; + case Qctl: + x = f->p[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + cb = parsecmd(a, n); + qlock(&c->l); + if(waserror()){ + qunlock(&c->l); + free(cb); + nexterror(); + } + if(cb->nf < 1) + error("short control request"); + if(strcmp(cb->f[0], "connect") == 0) + connectctlmsg(x, c, cb); + else if(strcmp(cb->f[0], "announce") == 0) + announcectlmsg(x, c, cb); + else if(strcmp(cb->f[0], "bind") == 0) + bindctlmsg(x, c, cb); + else if(strcmp(cb->f[0], "ttl") == 0){ + /* ignored */ + } else if(strcmp(cb->f[0], "tos") == 0){ + /* ignored */ + } else if(strcmp(cb->f[0], "ignoreadvice") == 0){ + /* ignored */ + } else if(strcmp(cb->f[0], "headers4") == 0){ + if(c->p->stype != S_UDP) + error(Enoctl); + c->headers = OUdphdrlenv4; + } else if(strcmp(cb->f[0], "oldheaders") == 0){ + if(c->p->stype != S_UDP) + error(Enoctl); + c->headers = OUdphdrlen; + } else if(strcmp(cb->f[0], "headers") == 0){ + if(c->p->stype != S_UDP) + error(Enoctl); + c->headers = Udphdrlen; + } else if(strcmp(cb->f[0], "hangup") == 0){ + if(c->p->stype != S_TCP) + error(Enoctl); + qunlock(&c->l); + if(waserror()){ + qlock(&c->l); + nexterror(); + } + /* TO DO: check fd status if socket close/hangup interrupted */ + if(c->sfd >= 0 && so_hangup(c->sfd, 1) < 0) + oserror(); + qlock(&c->l); + poperror(); + c->sfd = -1; + c->state = Hungup; + } else if(strcmp(cb->f[0], "keepalive") == 0){ + if(c->p->stype != S_TCP) + error(Enoctl); + if(c->sfd < 0) + error("not connected"); + so_keepalive(c->sfd, cb->nf>1? atoi(cb->f[1]): 0); + } else + error(Enoctl); + poperror(); + qunlock(&c->l); + free(cb); + break; + } + return n; +} + +static int +ipwstat(Chan *c, uchar *dp, int n) +{ + Dir *d; + Conv *cv; + Proto *p; + Fs *f; + + f = ipfs[c->dev]; + switch(TYPE(c->qid)) { + default: + error(Eperm); + break; + case Qctl: + case Qdata: + break; + } + + d = smalloc(sizeof(*d)+n); + if(waserror()){ + free(d); + nexterror(); + } + n = convM2D(dp, n, d, (char*)&d[1]); + if(n == 0) + error(Eshortstat); + p = f->p[PROTO(c->qid)]; + cv = p->conv[CONV(c->qid)]; + if(!iseve() && strcmp(up->env->user, cv->owner) != 0) + error(Eperm); + if(!emptystr(d->uid)) + kstrdup(&cv->owner, d->uid); + if(d->mode != ~0UL) + cv->perm = d->mode & 0777; + poperror(); + free(d); + return n; +} + +static Conv* +protoclone(Proto *p, char *user, int nfd) +{ + Conv *c, **pp, **ep, **np; + int maxconv; + + c = 0; + qlock(&p->l); + if(waserror()) { + qunlock(&p->l); + nexterror(); + } + ep = &p->conv[p->nc]; + for(pp = p->conv; pp < ep; pp++) { + c = *pp; + if(c == 0) { + c = newconv(p, pp); + break; + } + if(canqlock(&c->l)){ + if(c->inuse == 0) + break; + qunlock(&c->l); + } + } + if(pp >= ep) { + if(p->nc >= MAXCONV) { + qunlock(&p->l); + poperror(); + return 0; + } + maxconv = 2 * p->nc; + if(maxconv > MAXCONV) + maxconv = MAXCONV; + np = realloc(p->conv, sizeof(Conv*) * maxconv); + if(np == nil) + error(Enomem); + p->conv = np; + pp = &p->conv[p->nc]; + memset(pp, 0, sizeof(Conv*)*(maxconv - p->nc)); + p->nc = maxconv; + c = newconv(p, pp); + } + + c->inuse = 1; + kstrdup(&c->owner, user); + c->perm = 0660; + c->state = Idle; + ipmove(c->laddr, IPnoaddr); + ipmove(c->raddr, IPnoaddr); + c->lport = 0; + c->rport = 0; + c->restricted = 0; + c->sfd = nfd; + if(nfd == -1) + c->sfd = so_socket(p->stype); + + qunlock(&c->l); + qunlock(&p->l); + poperror(); + return c; +} + +static Conv* +newconv(Proto *p, Conv **pp) +{ + Conv *c; + + *pp = c = malloc(sizeof(Conv)); + if(c == 0) + error(Enomem); + qlock(&c->l); + c->inuse = 1; + c->p = p; + c->x = pp - p->conv; + p->ac++; + return c; +} + +int +arpwrite(char *s, int len) +{ + int n; + char *f[4], buf[256]; + + if(len >= sizeof(buf)) + len = sizeof(buf)-1; + memmove(buf, s, len); + buf[len] = 0; + if(len > 0 && buf[len-1] == '\n') + buf[len-1] = 0; + + n = getfields(buf, f, 4, 1, " "); + if(strcmp(f[0], "add") == 0) { + if(n == 3) { + arpadd(f[1], f[2], n); + return len; + } + } + error("bad arp request"); + + return len; +} + +Dev ipdevtab = { + 'I', + "ip", + + ipinit, + ipattach, + ipwalk, + ipstat, + ipopen, + devcreate, + ipclose, + ipread, + devbread, + ipwrite, + devbwrite, + devremove, + ipwstat +}; + +int +Fsproto(Fs *f, Proto *p) +{ + if(f->np >= Maxproto) + return -1; + + p->f = f; + + if(p->ipproto > 0){ + if(f->t2p[p->ipproto] != nil) + return -1; + f->t2p[p->ipproto] = p; + } + + p->qid.type = QTDIR; + p->qid.path = QID(f->np, 0, Qprotodir); + p->conv = malloc(sizeof(Conv*)*(p->nc+1)); + if(p->conv == nil) + panic("Fsproto"); + + p->x = f->np; + f->p[f->np++] = p; + + return 0; +} + +/* + * return true if this protocol is + * built in + */ +int +Fsbuiltinproto(Fs* f, uchar proto) +{ + return f->t2p[proto] != nil; +} + +/* + * temporarily convert ipv6 addresses to ipv4 as ulong for + * ipif.c interface + */ +static ulong +ip6w(uchar *a) +{ + uchar v4[IPv4addrlen]; + + v6tov4(v4, a); + return (((((v4[0]<<8)|v4[1])<<8)|v4[2])<<8)|v4[3]; +} + +static void +ipw6(uchar *a, ulong w) +{ + memmove(a, v4prefix, IPv4off); + hnputl(a+IPv4off, w); +} diff --git a/emu/port/devlogfs.c b/emu/port/devlogfs.c new file mode 100755 index 00000000..2d867496 --- /dev/null +++ b/emu/port/devlogfs.c @@ -0,0 +1,1522 @@ +#ifndef EMU +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#else +#include "error.h" +#endif +#include <dat.h> +#include <fns.h> +#include <kernel.h> +#include <logfs.h> +#include <nandfs.h> + +#ifndef EMU +#define Sleep sleep +#define Wakeup wakeup +#endif + +#ifndef offsetof +#define offsetof(T,X) ((ulong)&(((T*)0)->X)) +#endif + +typedef struct Devlogfs Devlogfs; +typedef struct DevlogfsSession DevlogfsSession; + +//#define CALLTRACE + +enum { + DEVLOGFSDEBUG = 0, + DEVLOGFSIODEBUG = 0, + DEVLOGFSBAD = 1, +}; + +enum { + Qdir, + Qctl, + Qusers, + Qdump, + Qfs, + Qfsboot, + Qend, +}; + +typedef enum DevlogfsServerState { Closed, BootOpen, NeedVersion, NeedAttach, Attached, Hungup } DevlogfsServerState; + +struct Devlogfs { + QLock qlock; + Ref ref; + int instance; + int trace; /* (debugging) trace of read/write actions */ + int nand; + char *name; + char *device; + char *filename[Qend - Qfs]; + LogfsLowLevel *ll; + Chan *flash, *flashctl; + QLock bootqlock; + int logfstrace; + LogfsBoot *lb; + /* stuff for server */ + ulong openflags; + Fcall in; + Fcall out; + int reading; + DevlogfsServerState state; + Rendez readrendez; + Rendez writerendez; + uint readcount; + ulong readbufsize; + uchar *readbuf; + uchar *readp; + LogfsServer *server; + Devlogfs *next; +}; + +#define MAXMSIZE 8192 + +static struct { + RWlock rwlock; /* rlock when walking, wlock when changing */ + QLock configqlock; /* serialises addition of new configurations */ + Devlogfs *head; + char *defname; +} devlogfslist; + +static LogfsIdentityStore *is; + +#ifndef EMU +char Eunknown[] = "unknown user or group id"; +#endif + +static void devlogfsfree(Devlogfs*); + +#define SPLITPATH(path, qtype, instance, qid, qt) { instance = path >> 4; qid = path & 0xf; qt = qtype & QTDIR; } +#define DATAQID(q, qt) (!(qt) && (q) >= Qfs && (q) < Qend) +#define MKPATH(instance, qid) ((instance << 4) | qid) + +#define PREFIX "logfs" + +static char *devlogfsprefix = PREFIX; +static char *devlogfsctlname = PREFIX "ctl"; +static char *devlogfsusersname = PREFIX "users"; +static char *devlogfsdumpname = PREFIX "dump"; +static char *devlogfsbootsuffix = "boot"; +static char *devlogfs9pversion = "9P2000"; + +enum { + Toshiba = 0x98, + Samsung = 0xec, +}; + +static struct { + uchar manufacturer; + uchar device; +} nandtab[] = { + { 0, 0xe6 }, + { 0, 0xea }, + { 0, 0xe3 }, + { 0, 0xe5 }, + { 0, 0x73 }, + { 0, 0x75 }, + { 0, 0x76 }, +}; + +static void +errorany(char *errmsg) +{ + if (errmsg) + error(errmsg); +} + +static void * +emalloc(ulong size) +{ + void *p; + p = logfsrealloc(nil, size); + if (p == nil) + error(Enomem); + return p; +} + +static char * +estrdup(char *q) +{ + void *p; + if (q == nil) + return nil; + p = logfsrealloc(nil, strlen(q) + 1); + if (p == nil) + error(Enomem); + return strcpy(p, q); +} + +static char * +estrconcat(char *a, ...) +{ + va_list l; + char *p, *r; + int t; + + t = strlen(a); + va_start(l, a); + while ((p = va_arg(l, char *)) != nil) + t += strlen(p); + + r = logfsrealloc(nil, t + 1); + if (r == nil) + error(Enomem); + + strcpy(r, a); + va_start(l, a); + while ((p = va_arg(l, char *)) != nil) + strcat(r, p); + + va_end(l); + + return r; +} + +static int +gen(Chan *c, int i, Dir *dp, int lockit) +{ + Devlogfs *l; + long size; + Qid qid; + qid.vers = 0; + qid.type = 0; + + if (i + Qctl < Qfs) { + switch (i + Qctl) { + case Qctl: + qid.path = Qctl; + devdir(c, qid, devlogfsctlname, 0, eve, 0666, dp); + return 1; + case Qusers: + qid.path = Qusers; + devdir(c, qid, devlogfsusersname, 0, eve, 0444, dp); + return 1; + case Qdump: + qid.path = Qdump; + devdir(c, qid, devlogfsdumpname, 0, eve, 0444, dp); + return 1; + } + } + + i -= Qfs - Qctl; + + if (lockit) + rlock(&devlogfslist.rwlock); + + if (waserror()) { + if (lockit) + runlock(&devlogfslist.rwlock); + nexterror(); + } + + for (l = devlogfslist.head; l; l = l->next) { + if (i < Qend - Qfs) + break; + i -= Qend - Qfs; + } + + if (l == nil) { + poperror(); + if (lockit) + runlock(&devlogfslist.rwlock); + return -1; + } + + switch (Qfs + i) { + case Qfsboot: + size = l->lb ? logfsbootgetsize(l->lb) : 0; + break; + default: + size = 0; + break; + } + /* perhaps the user id should come from the underlying file */ + qid.path = MKPATH(l->instance, Qfs + i); + devdir(c, qid, l->filename[i], size, eve, 0666, dp); + + poperror(); + if (lockit) + runlock(&devlogfslist.rwlock); + + return 1; +} + +static int +devlogfsgen(Chan *c, char *n, Dirtab *tab, int ntab, int i, Dir *dp) +{ + USED(n); + USED(tab); + USED(ntab); + return gen(c, i, dp, 1); +} + +static int +devlogfsgennolock(Chan *c, char *n, Dirtab *tab, int ntab, int i, Dir *dp) +{ + USED(n); + USED(tab); + USED(ntab); + return gen(c, i, dp, 0); +} + +/* called under lock */ +static Devlogfs * +devlogfsfind(int instance) +{ + Devlogfs *l; + + for (l = devlogfslist.head; l; l = l->next) + if (l->instance == instance) + break; + return l; +} + +static Devlogfs * +devlogfsget(int instance) +{ + Devlogfs *l; + rlock(&devlogfslist.rwlock); + for (l = devlogfslist.head; l; l = l->next) + if (l->instance == instance) + break; + if (l) + incref(&l->ref); + runlock(&devlogfslist.rwlock); + return l; +} + +static Devlogfs * +devlogfsfindbyname(char *name) +{ + Devlogfs *l; + + rlock(&devlogfslist.rwlock); + for (l = devlogfslist.head; l; l = l->next) + if (strcmp(l->name, name) == 0) + break; + runlock(&devlogfslist.rwlock); + return l; +} + +static Devlogfs * +devlogfssetdefname(char *name) +{ + Devlogfs *l; + char *searchname; + wlock(&devlogfslist.rwlock); + if (waserror()) { + wunlock(&devlogfslist.rwlock); + nexterror(); + } + if (name == nil) + searchname = devlogfslist.defname; + else + searchname = name; + for (l = devlogfslist.head; l; l = l->next) + if (strcmp(l->name, searchname) == 0) + break; + if (l == nil) { + logfsfreemem(devlogfslist.defname); + devlogfslist.defname = nil; + } + else if (name) { + if (devlogfslist.defname) { + logfsfreemem(devlogfslist.defname); + devlogfslist.defname = nil; + } + devlogfslist.defname = estrdup(name); + } + poperror(); + wunlock(&devlogfslist.rwlock); + return l; +} + +static Chan * +devlogfskopen(char *name, char *suffix, int mode) +{ + Chan *c; + char *fn; + int fd; + + fn = estrconcat(name, suffix, 0); + fd = kopen(fn, mode); + logfsfreemem(fn); + if (fd < 0) + error(up->env->errstr); + c = fdtochan(up->env->fgrp, fd, mode, 0, 1); + kclose(fd); + return c; +} + +static char * +xread(void *a, void *buf, long nbytes, ulong offset) +{ + Devlogfs *l = a; + long rv; + + if (DEVLOGFSIODEBUG || l->trace) + print("devlogfs: %s: read(0x%lux, %ld)\n", l->device, offset, nbytes); + l->flash->offset = offset; + rv = kchanio(l->flash, buf, nbytes, OREAD); + if (rv < 0) { + print("devlogfs: %s: flash read error: %s\n", l->device, up->env->errstr); + return up->env->errstr; + } + if (rv != nbytes) { + print("devlogfs: %s: short flash read: offset %lud, %ld not %ld\n", l->device, offset, rv, nbytes); + return "short read"; + } + return nil; +} + +static char * +xwrite(void *a, void *buf, long nbytes, ulong offset) +{ + Devlogfs *l = a; + long rv; + + if (DEVLOGFSIODEBUG || l->trace) + print("devlogfs: %s: write(0x%lux, %ld)\n", l->device, offset, nbytes); + l->flash->offset = offset; + rv = kchanio(l->flash, buf, nbytes, OWRITE); + if (rv < 0) { + print("devlogfs: %s: flash write error: %s\n", l->device, up->env->errstr); + return up->env->errstr; + } + if (rv != nbytes) { + print("devlogfs: %s: short flash write: offset %lud, %ld not %ld\n", l->device, offset, rv, nbytes); + return "short write"; + } + return nil; +} + +static char * +xerase(void *a, long address) +{ + Devlogfs *l = a; + char cmd[40]; + + if (DEVLOGFSIODEBUG || l->trace) + print("devlogfs: %s: erase(0x%lux)\n", l->device, address); + snprint(cmd, sizeof(cmd), "erase 0x%8.8lux", address); + if (kchanio(l->flashctl, cmd, strlen(cmd), OWRITE) <= 0) { + print("devlogfs: %s: flash erase error: %s\n", l->device, up->env->errstr); + return up->env->errstr; + } + return nil; +} + +static char * +xsync(void *a) +{ + Devlogfs *l = a; + uchar statbuf[STATFIXLEN]; + + if (DEVLOGFSIODEBUG || l->trace) + print("devlogfs: %s: sync()\n", l->device); + memset(statbuf, 0xff, sizeof(statbuf)); + memset(statbuf + STATFIXLEN - 8, 0x00, 8); + PBIT16(statbuf, sizeof(statbuf) - BIT16SZ); + if (kwstat(l->device, statbuf, sizeof(statbuf)) < 0) + return up->env->errstr; + return nil; +} + +//#define LEAKHUNT +#ifdef LEAKHUNT +#define MAXLIVE 2000 +typedef struct Live { + void *p; + int freed; + ulong callerpc; +} Live; + +static Live livemem[MAXLIVE]; + +static void +leakalloc(void *p, ulong callerpc) +{ + int x; + int use = -1; + for (x = 0; x < MAXLIVE; x++) { + if (livemem[x].p == p) { + if (!livemem[x].freed) + print("leakalloc: unexpected realloc of 0x%.8lux from 0x%.8lux\n", p, callerpc); +// else +// print("leakalloc: reusing address 0x%.8lux from 0x%.8lux\n", p, callerpc); + livemem[x].freed = 0; + livemem[x].callerpc = callerpc; + return; + } + else if (use < 0 && livemem[x].p == 0) + use = x; + } + if (use < 0) + panic("leakalloc: too many live entries"); + livemem[use].p = p; + livemem[use].freed = 0; + livemem[use].callerpc = callerpc; +} + +static void +leakaudit(void) +{ + int x; + for (x = 0; x < MAXLIVE; x++) { + if (livemem[x].p && !livemem[x].freed) + print("leakaudit: 0x%.8lux from 0x%.8lux\n", livemem[x].p, livemem[x].callerpc); + } +} + +static void +leakfree(void *p, ulong callerpc) +{ + int x; + if (p == nil) + return; + for (x = 0; x < MAXLIVE; x++) { + if (livemem[x].p == p) { + if (livemem[x].freed) + print("leakfree: double free of 0x%.8lux from 0x%.8lux, originally by 0x%.8lux\n", + p, callerpc, livemem[x].callerpc); + livemem[x].freed = 1; + livemem[x].callerpc = callerpc; + return; + } + } + print("leakfree: free of unalloced address 0x%.8lux from 0x%.8lux\n", p, callerpc); + leakaudit(); +} + +static void +leakrealloc(void *newp, void *oldp, ulong callerpc) +{ + leakfree(oldp, callerpc); + leakalloc(newp, callerpc); +} +#endif + + +#ifdef LEAKHUNT +static void *_realloc(void *p, ulong size, ulong callerpc) +#else +void * +logfsrealloc(void *p, ulong size) +#endif +{ + void *q; + ulong osize; + if (waserror()) { + print("wobbly thrown in memory allocator: %s\n", up->env->errstr); + nexterror(); + } + if (p == nil) { + q = smalloc(size); + poperror(); +#ifdef LEAKHUNT + leakrealloc(q, nil, callerpc); +#endif + return q; + } + q = realloc(p, size); + if (q) { + poperror(); +#ifdef LEAKHUNT + leakrealloc(q, p, callerpc); +#endif + return q; + } + q = smalloc(size); + osize = msize(p); + if (osize > size) + osize = size; + memmove(q, p, osize); + free(p); + poperror(); +#ifdef LEAKHUNT + leakrealloc(q, p, callerpc); +#endif + return q; +} + +#ifdef LEAKHUNT +void * +logfsrealloc(void *p, ulong size) +{ + return _realloc(p, size, getcallerpc(&p)); +} + +void * +nandfsrealloc(void *p, ulong size) +{ + return _realloc(p, size, getcallerpc(&p)); +} +#else +void * +nandfsrealloc(void *p, ulong size) +{ + return logfsrealloc(p, size); +} +#endif + +void +logfsfreemem(void *p) +{ +#ifdef LEAKHUNT + leakfree(p, getcallerpc(&p)); +#endif + free(p); +} + +void +nandfsfreemem(void *p) +{ +#ifdef LEAKHUNT + leakfree(p, getcallerpc(&p)); +#endif + free(p); +} + +static Devlogfs * +devlogfsconfig(char *name, char *device) +{ + Devlogfs *newl, *l; + int i; + int n; + char buf[100], *fields[8]; + long rawblocksize, rawsize; + + newl = nil; + + qlock(&devlogfslist.configqlock); + + if (waserror()) { + qunlock(&devlogfslist.configqlock); + devlogfsfree(newl); + nexterror(); + } + + rlock(&devlogfslist.rwlock); + for (l = devlogfslist.head; l; l = l->next) + if (strcmp(l->name, name) == 0) { + runlock(&devlogfslist.rwlock); + error(Einuse); + } + + /* horrid n^2 solution to finding a unique instance number */ + + for (i = 0;; i++) { + for (l = devlogfslist.head; l; l = l->next) + if (l->instance == i) + break; + if (l == nil) + break; + } + runlock(&devlogfslist.rwlock); + + newl = emalloc(sizeof(Devlogfs)); + newl->instance = i; + newl->name = estrdup(name); + newl->device = estrdup(device); + newl->filename[Qfs - Qfs] = estrconcat(devlogfsprefix, name, nil); + newl->filename[Qfsboot - Qfs] = estrconcat(devlogfsprefix, name, devlogfsbootsuffix, nil); + newl->flash = devlogfskopen(device, nil, ORDWR); + newl->flashctl = devlogfskopen(device, "ctl", ORDWR); + newl->flashctl->offset = 0; + if ((n = kchanio(newl->flashctl, buf, sizeof(buf), OREAD)) <= 0) { + print("devlogfsconfig: read ctl failed: %s\n", up->env->errstr); + error(up->env->errstr); + } + + if (n >= sizeof(buf)) + n = sizeof(buf) - 1; + buf[n] = 0; + n = getfields(buf, fields, nelem(fields), 1, " \t\n"); + newl->nand = 0; + if (n >= 2) { + /* detect NAND devices, and learn parameters from there */ + ulong manufacturer = strtoul(fields[0], nil, 16); + ulong device = strtoul(fields[1], nil, 16); + int d; + + for (d = 0; d < sizeof(nandtab) / sizeof(nandtab[0]); d++) { + if ((nandtab[d].manufacturer == manufacturer + && nandtab[d].device == device) + || (nandtab[d].manufacturer == 0 + && (manufacturer == Toshiba || manufacturer == Samsung) + && nandtab[d].device == device)) + { + if (DEVLOGFSDEBUG) + print("devlogfsconfig: nand device detected\n"); + newl->nand = 1; + break; + } + } + } + if (n < 4) + error("unknown erase size"); + rawblocksize = strtol(fields[5], nil, 0); + rawsize = strtol(fields[4], nil, 0)-strtol(fields[3], nil, 0); + if (newl->nand == 0) + error("only NAND supported at the moment"); + errorany(nandfsinit(newl, rawsize, rawblocksize, xread, xwrite, xerase, xsync, &newl->ll)); + wlock(&devlogfslist.rwlock); + newl->next = devlogfslist.head; + devlogfslist.head = newl; + logfsfreemem(devlogfslist.defname); + devlogfslist.defname = nil; + if (waserror()) { + } + else { + devlogfslist.defname = estrdup(name); + poperror(); + } + wunlock(&devlogfslist.rwlock); + poperror(); + qunlock(&devlogfslist.configqlock); + return newl; +} + +void +devlogfsunconfig(Devlogfs *devlogfs) +{ + Devlogfs **lp; + + qlock(&devlogfslist.configqlock); + + if (waserror()) { + qunlock(&devlogfslist.configqlock); + nexterror(); + } + + wlock(&devlogfslist.rwlock); + + if (waserror()) { + wunlock(&devlogfslist.rwlock); + nexterror(); + } + + for (lp = &devlogfslist.head; *lp && (*lp) != devlogfs; lp = &(*lp)->next) + ; + if (*lp == nil) { + if (DEVLOGFSBAD) + print("devlogfsunconfig: not in list\n"); + } + else + *lp = devlogfs->next; + + poperror(); + wunlock(&devlogfslist.rwlock); + + /* now invisible to the naked eye */ + devlogfsfree(devlogfs); + poperror(); + qunlock(&devlogfslist.configqlock); +} + +static void +devlogfsllopen(Devlogfs *l) +{ + qlock(&l->qlock); + if (waserror()) { + qunlock(&l->qlock); + nexterror(); + } + if (l->lb == nil) + errorany(logfsbootopen(l->ll, 0, 0, l->logfstrace, 1, &l->lb)); + l->state = BootOpen; + poperror(); + qunlock(&l->qlock); +} + +static void +devlogfsllformat(Devlogfs *l, long bootsize) +{ + qlock(&l->qlock); + if (waserror()) { + qunlock(&l->qlock); + nexterror(); + } + if (l->lb == nil) + errorany(logfsformat(l->ll, 0, 0, bootsize, l->logfstrace)); + poperror(); + qunlock(&l->qlock); +} + +static Chan * +devlogfsattach(char *spec) +{ + Chan *c; +#ifdef CALLTRACE + print("devlogfsattach(spec = %s) - start\n", spec); +#endif + /* create the identity store on first attach */ + if (is == nil) + errorany(logfsisnew(&is)); + c = devattach(0x29f, spec); +// c = devattach(L'ʟ', spec); +#ifdef CALLTRACE + print("devlogfsattach(spec = %s) - return %.8lux\n", spec, (ulong)c); +#endif + return c; +} + +static Walkqid* +devlogfswalk(Chan *c, Chan *nc, char **name, int nname) +{ + int instance, qid, qt, clone; + Walkqid *wq; + +#ifdef CALLTRACE + print("devlogfswalk(c = 0x%.8lux, nc = 0x%.8lux, name = 0x%.8lux, nname = %d) - start\n", + (ulong)c, (ulong)nc, (ulong)name, nname); +#endif + clone = 0; + if(nc == nil){ + nc = devclone(c); + nc->type = 0; + SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt); + if(DATAQID(qid, qt)) + nc->aux = devlogfsget(instance); + clone = 1; + } + wq = devwalk(c, nc, name, nname, 0, 0, devlogfsgen); + if (wq == nil || wq->nqid < nname) { + if(clone) + cclose(nc); + } + else if (clone) { + wq->clone = nc; + nc->type = c->type; + } +#ifdef CALLTRACE + print("devlogfswalk(c = 0x%.8lux, nc = 0x%.8lux, name = 0x%.8lux, nname = %d) - return\n", + (ulong)c, (ulong)nc, (ulong)name, nname); +#endif + return wq; +} + +static int +devlogfsstat(Chan *c, uchar *dp, int n) +{ +#ifdef CALLTRACE + print("devlogfsstat(c = 0x%.8lux, dp = 0x%.8lux n= %d)\n", + (ulong)c, (ulong)dp, n); +#endif + return devstat(c, dp, n, 0, 0, devlogfsgen); +} + +static Chan* +devlogfsopen(Chan *c, int omode) +{ + int instance, qid, qt; + + omode = openmode(omode); + SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt); +#ifdef CALLTRACE + print("devlogfsopen(c = 0x%.8lux, omode = %o, instance = %d, qid = %d, qt = %d)\n", + (ulong)c, omode, instance, qid, qt); +#endif + + + rlock(&devlogfslist.rwlock); + if (waserror()) { + runlock(&devlogfslist.rwlock); +#ifdef CALLTRACE + print("devlogfsopen(c = 0x%.8lux, omode = %o) - error %s\n", (ulong)c, omode, up->env->errstr); +#endif + nexterror(); + } + + if (DATAQID(qid, qt)) { + Devlogfs *d; + d = devlogfsfind(instance); + if (d == nil) + error(Enodev); + if (strcmp(up->env->user, eve) != 0) + error(Eperm); + if (qid == Qfs && d->state != BootOpen) + error(Eperm); + if (d->server == nil) { + errorany(logfsservernew(d->lb, d->ll, is, d->openflags, d->logfstrace, &d->server)); + d->state = NeedVersion; + } + c = devopen(c, omode, 0, 0, devlogfsgennolock); + incref(&d->ref); + c->aux = d; + } + else if (qid == Qctl || qid == Qusers) { + if (strcmp(up->env->user, eve) != 0) + error(Eperm); + c = devopen(c, omode, 0, 0, devlogfsgennolock); + } + else + c = devopen(c, omode, 0, 0, devlogfsgennolock); + poperror(); + runlock(&devlogfslist.rwlock); +#ifdef CALLTRACE + print("devlogfsopen(c = 0x%.8lux, omode = %o) - return\n", (ulong)c, omode); +#endif + return c; +} + +static void +devlogfsclose(Chan *c) +{ + int instance, qid, qt; +#ifdef CALLTRACE + print("devlogfsclose(c = 0x%.8lux)\n", (ulong)c); +#endif + SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt); + USED(instance); + if(DATAQID(qid, qt) && (c->flag & COPEN) != 0) { + Devlogfs *d; + d = c->aux; + qlock(&d->qlock); + if (qid == Qfs && d->state == Attached) { + logfsserverflush(d->server); + logfsserverfree(&d->server); + d->state = BootOpen; + } + qunlock(&d->qlock); + decref(&d->ref); + } +#ifdef CALLTRACE + print("devlogfsclose(c = 0x%.8lux) - return\n", (ulong)c); +#endif +} + +typedef char *(SMARTIOFN)(void *magic, void *buf, long n, ulong offset, int write); + +void +smartio(SMARTIOFN *io, void *magic, void *buf, long n, ulong offset, long blocksize, int write) +{ + void *tmp = nil; + ulong blocks, toread; + + if (waserror()) { + logfsfreemem(tmp); + nexterror(); + } + if (offset % blocksize) { + ulong aoffset; + int tmpoffset; + int tocopy; + + if (tmp == nil) + tmp = emalloc(blocksize); + aoffset = offset / blocksize; + aoffset *= blocksize; + errorany((*io)(magic, tmp, blocksize, aoffset, 0)); + tmpoffset = offset - aoffset; + tocopy = blocksize - tmpoffset; + if (tocopy > n) + tocopy = n; + if (write) { + memmove((uchar *)tmp + tmpoffset, buf, tocopy); + errorany((*io)(magic, tmp, blocksize, aoffset, 1)); + } + else + memmove(buf, (uchar *)tmp + tmpoffset, tocopy); + buf = (uchar *)buf + tocopy; + n -= tocopy; + offset = aoffset + blocksize; + } + blocks = n / blocksize; + toread = blocks * blocksize; + errorany((*io)(magic, buf, toread, offset, write)); + buf = (uchar *)buf + toread; + n -= toread; + offset += toread; + if (n) { + if (tmp == nil) + tmp = emalloc(blocksize); + errorany((*io)(magic, tmp, blocksize, offset, 0)); + if (write) { + memmove(tmp, buf, n); + errorany((*io)(magic, tmp, blocksize, offset, 1)); + } + memmove(buf, tmp, n); + } + poperror(); + logfsfreemem(tmp); +} + +static int +readok(void *a) +{ + Devlogfs *d = a; + return d->reading; +} + +static int +writeok(void *a) +{ + Devlogfs *d = a; + return !d->reading; +} + +long +devlogfsserverread(Devlogfs *d, void *buf, long n) +{ + if (d->state == Hungup) + error(Ehungup); + Sleep(&d->readrendez, readok, d); + if (n > d->readcount) + n = d->readcount; + memmove(buf, d->readp, n); + d->readp += n; + d->readcount -= n; + if (d->readcount == 0) { + d->reading = 0; + Wakeup(&d->writerendez); + } + return n; +} + +static void +reply(Devlogfs *d) +{ + d->readp = d->readbuf; + d->readcount = convS2M(&d->out, d->readp, d->readbufsize); +//print("reply is %d bytes\n", d->readcount); + if (d->readcount == 0) + panic("logfs: reply: did not fit\n"); + d->reading = 1; + Wakeup(&d->readrendez); +} + +static void +rerror(Devlogfs *d, char *ename) +{ + d->out.type = Rerror; + d->out.ename = ename; + reply(d); +} + +static struct { + QLock qlock; + int (*read)(void *magic, Devlogfs *d, int line, char *buf, int buflen); + void *magic; + Devlogfs *d; + int line; +} dump; + +static void * +extentdumpinit(Devlogfs *d, int argc, char **argv) +{ + int *p; + ulong path; + ulong flashaddr, length; + long block; + int page, offset; + if (argc != 1) + error(Ebadarg); + path = strtoul(argv[0], 0, 0); + errorany(logfsserverreadpathextent(d->server, path, 0, &flashaddr, &length, &block, &page, &offset)); + p = emalloc(sizeof(ulong)); + *p = path; + return p; +} + +static int +extentdumpread(void *magic, Devlogfs *d, int line, char *buf, int buflen) +{ + ulong *p = magic; + ulong flashaddr, length; + long block; + int page, offset; + USED(d); + errorany(logfsserverreadpathextent(d->server, *p, line, &flashaddr, &length, &block, &page, &offset)); + if (length == 0) + return 0; + return snprint(buf, buflen, "%.8ux %ud %ld %d %d\n", flashaddr, length, block, page, offset); +} + +void +devlogfsdumpinit(Devlogfs *d, + void *(*init)(Devlogfs *d, int argc, char **argv), + int (*read)(void *magic, Devlogfs *d, int line, char *buf, int buflen), int argc, char **argv) +{ + qlock(&dump.qlock); + if (waserror()) { + qunlock(&dump.qlock); + nexterror(); + } + if (d) { + if (d->state < NeedVersion) + error("not mounted"); + qlock(&d->qlock); + if (waserror()) { + qunlock(&d->qlock); + nexterror(); + } + } + if (dump.magic) { + logfsfreemem(dump.magic); + dump.magic = nil; + } + dump.d = d; + dump.magic = (*init)(d, argc, argv); + dump.read = read; + dump.line = 0; + if (d) { + poperror(); + qunlock(&d->qlock); + } + poperror(); + qunlock(&dump.qlock); +} + +long +devlogfsdumpread(char *buf, int buflen) +{ + char *tmp = nil; + long n; + qlock(&dump.qlock); + if (waserror()) { + logfsfreemem(tmp); + qunlock(&dump.qlock); + nexterror(); + } + if (dump.magic == nil) + error(Eio); + tmp = emalloc(READSTR); + if (dump.d) { + if (dump.d->state < NeedVersion) + error("not mounted"); + qlock(&dump.d->qlock); + if (waserror()) { + qunlock(&dump.d->qlock); + nexterror(); + } + } + n = (*dump.read)(dump.magic, dump.d, dump.line, tmp, READSTR); + if (n) { + dump.line++; + n = readstr(0, buf, buflen, tmp); + } + if (dump.d) { + poperror(); + qunlock(&dump.d->qlock); + } + logfsfreemem(tmp); + poperror(); + qunlock(&dump.qlock); + return n; +} + +void +devlogfsserverlogsweep(Devlogfs *d, int justone) +{ + int didsomething; + if (d->state < NeedVersion) + error("not mounted"); + qlock(&d->qlock); + if (waserror()) { + qunlock(&d->qlock); + nexterror(); + } + errorany(logfsserverlogsweep(d->server, justone, &didsomething)); + poperror(); + qunlock(&d->qlock); +} + +void +devlogfsserverwrite(Devlogfs *d, void *buf, long n) +{ + int locked = 0; + if (d->state == Hungup) + error(Ehungup); + Sleep(&d->writerendez, writeok, d); + if (convM2S(buf, n, &d->in) != n) { + /* + * someone is writing drivel; have nothing to do with them anymore + * most common cause; trying to mount authenticated + */ + d->state = Hungup; + error(Ehungup); + } + d->out.tag = d->in.tag; + d->out.fid = d->in.fid; + d->out.type = d->in.type + 1; + if (waserror()) { + if (locked) + qunlock(&d->qlock); + rerror(d, up->env->errstr); + return; + } + if (d->in.type != Tversion && d->in.type != Tattach) { + if (d->state != Attached) + error("must be attached"); + qlock(&d->qlock); + locked = 1; + } + switch (d->in.type) { + case Tauth: + error("no authentication needed"); + case Tversion: { + char *rversion; + if (d->state != NeedVersion) + error("unexpected Tversion"); + if (d->in.tag != NOTAG) + error("protocol botch"); + /* + * check the version string + */ + if (strcmp(d->in.version, devlogfs9pversion) != 0) + rversion = "unknown"; + else + rversion = devlogfs9pversion; + /* + * allocate the reply buffer + */ + d->readbufsize = d->in.msize; + if (d->readbufsize > MAXMSIZE) + d->readbufsize = MAXMSIZE; + d->readbuf = emalloc(d->readbufsize); + /* + * compose the Rversion + */ + d->out.msize = d->readbufsize; + d->out.version = rversion; + d->state = NeedAttach; + break; + } + case Tattach: + if (d->state != NeedAttach) + error("unexpected attach"); + if (d->in.afid != NOFID) + error("unexpected afid"); + errorany(logfsserverattach(d->server, d->in.fid, d->in.uname, &d->out.qid)); + d->state = Attached; + break; + case Tclunk: + errorany(logfsserverclunk(d->server, d->in.fid)); + break; + case Tcreate: + errorany(logfsservercreate(d->server, d->in.fid, d->in.name, d->in.perm, d->in.mode, &d->out.qid)); + d->out.iounit = d->readbufsize - 11; + break; + case Tflush: + break; + case Topen: + errorany(logfsserveropen(d->server, d->in.fid, d->in.mode, &d->out.qid)); + d->out.iounit = d->readbufsize - 11; + break; + case Tread: + d->out.data = (char *)d->readbuf + 11; + /* TODO - avoid memmove */ + errorany(logfsserverread(d->server, d->in.fid, d->in.offset, d->in.count, (uchar *)d->out.data, + d->readbufsize - 11, &d->out.count)); + break; + case Tremove: + errorany(logfsserverremove(d->server, d->in.fid)); + break; + case Tstat: + d->out.stat = d->readbuf + 9; + /* TODO - avoid memmove */ + errorany(logfsserverstat(d->server, d->in.fid, d->out.stat, d->readbufsize - 9, &d->out.nstat)); +// print("nstat %d\n", d->out.nstat); + break; + case Twalk: + errorany(logfsserverwalk(d->server, d->in.fid, d->in.newfid, + d->in.nwname, d->in.wname, &d->out.nwqid, d->out.wqid)); + break; + case Twrite: + errorany(logfsserverwrite(d->server, d->in.fid, d->in.offset, d->in.count, (uchar *)d->in.data, + &d->out.count)); + break; + case Twstat: + errorany(logfsserverwstat(d->server, d->in.fid, d->in.stat, d->in.nstat)); + break; + default: + print("devlogfsserverwrite: msg %d unimplemented\n", d->in.type); + error("unimplemented"); + } + poperror(); + if (locked) + qunlock(&d->qlock); + reply(d); +} + +static long +devlogfsread(Chan *c, void *buf, long n, vlong off) +{ + int instance, qid, qt; + + SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt); + USED(instance); +#ifdef CALLTRACE + print("devlogfsread(c = 0x%.8lux, buf = 0x%.8lux, n = %ld, instance = %d, qid = %d, qt = %d) - start\n", + (ulong)c, (ulong)buf, n, instance, qid, qt); +#endif + if(qt & QTDIR) { +#ifdef CALLTRACE + print("devlogfsread(c = 0x%.8lux, buf = 0x%.8lux, n = %ld, instance = %d, qid = %d, qt = %d) - calling devdirread\n", + (ulong)c, (ulong)buf, n, instance, qid, qt); +#endif + return devdirread(c, buf, n, 0, 0, devlogfsgen); + } + + if(DATAQID(qid, qt)) { + if (qid == Qfsboot) { + Devlogfs *l = c->aux; + qlock(&l->bootqlock); + if (waserror()) { + qunlock(&l->bootqlock); + nexterror(); + } + smartio((SMARTIOFN *)logfsbootio, l->lb, buf, n, off, logfsbootgetiosize(l->lb), 0); + poperror(); + qunlock(&l->bootqlock); + return n; + } + else if (qid == Qfs) { + Devlogfs *d = c->aux; + return devlogfsserverread(d, buf, n); + } + error(Eio); + } + + if (qid == Qusers) { + long nr; + errorany(logfsisusersread(is, buf, n, (ulong)off, &nr)); + return nr; + } + else if (qid == Qdump) + return devlogfsdumpread(buf, n); + + if (qid != Qctl) + error(Egreg); + + return 0; +} + +static long +devlogfswrite(Chan *c, void *buf, long n, vlong off) +{ + char cmd[64], *realfields[6]; + int i; + int instance, qid, qt; + + if(n <= 0) + return 0; + SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt); +#ifdef CALLTRACE + print("devlogfswrite(c = 0x%.8lux, buf = 0x%.8lux, n = %ld, instance = %d, qid = %d, qt = %d) - start\n", + (ulong)c, (ulong)buf, n, instance, qid, qt); +#endif + USED(instance); + if(DATAQID(qid, qt)){ + if (qid == Qfsboot) { + Devlogfs *l = c->aux; + qlock(&l->bootqlock); + if (waserror()) { + qunlock(&l->bootqlock); + nexterror(); + } + smartio((SMARTIOFN *)logfsbootio, l->lb, buf, n, off, logfsbootgetiosize(l->lb), 1); + poperror(); + qunlock(&l->bootqlock); + return n; + } + else if (qid == Qfs) { + Devlogfs *d = c->aux; + devlogfsserverwrite(d, buf, n); + return n; + } + error(Eio); + } + else if (qid == Qctl) { + Devlogfs *l = nil; + char **fields; + + if(n > sizeof(cmd)-1) + n = sizeof(cmd)-1; + memmove(cmd, buf, n); + cmd[n] = 0; + i = getfields(cmd, realfields, 6, 1, " \t\n"); +//print("i = %d\n", i); + if (i <= 0) + error(Ebadarg); + fields = realfields; + if (i == 3 && strcmp(fields[0], "uname") == 0) { + switch (fields[2][0]) { + default: + errorany(logfsisgroupcreate(is, fields[1], fields[2])); + break; + case ':': + errorany(logfsisgroupcreate(is, fields[1], fields[2] + 1)); + break; + case '%': + errorany(logfsisgrouprename(is, fields[1], fields[2] + 1)); + break; + case '=': + errorany(logfsisgroupsetleader(is, fields[1], fields[2] + 1)); + break; + case '+': + errorany(logfsisgroupaddmember(is, fields[1], fields[2] + 1)); + break; + case '-': + errorany(logfsisgroupremovemember(is, fields[1], fields[2] + 1)); + break; + } + i = 0; + } + if (i == 4 && strcmp(fields[0], "fsys") == 0 && strcmp(fields[2], "config") == 0) { + l = devlogfsconfig(fields[1], fields[3]); + i = 0; + } + else if (i >= 2 && strcmp(fields[0], "fsys") == 0) { + l = devlogfssetdefname(fields[1]); + if (l == nil) + error(Ebadarg); + i -= 2; + fields += 2; + } + if (i != 0) { + if (l == nil) + l = devlogfssetdefname(nil); + if (i >= 1 && strcmp(fields[0], "open") == 0) { + int a; + if (l == nil) + error(Ebadarg); + for (a = 1; a < i; a++) + if (fields[a][0] == '-') + switch (fields[a][1]) { + case 'P': + l->openflags |= LogfsOpenFlagNoPerm; + break; + case 'W': + l->openflags |= LogfsOpenFlagWstatAllow; + break; + default: + error(Ebadarg); + } + devlogfsllopen(l); + i = 0; + } + else if (i == 2 && strcmp(fields[0], "format") == 0) { + if (l == nil) + error(Ebadarg); + devlogfsllformat(l, strtol(fields[1], nil, 0)); + i = 0; + } + else if (i >= 1 && strcmp(fields[0], "sweep") == 0) { + if (l == nil) + error(Ebadarg); + devlogfsserverlogsweep(l, 0); + i = 0; + } + else if (i >= 1 && strcmp(fields[0], "sweepone") == 0) { + if (l == nil) + error(Ebadarg); + devlogfsserverlogsweep(l, 1); + i = 0; + } + else if (i <= 2&& strcmp(fields[0], "trace") == 0) { + if (l == nil) + error(Ebadarg); + l->logfstrace = i > 1 ? strtol(fields[1], nil, 0) : 0; + if (l->server) + logfsservertrace(l->server, l->logfstrace); + if (l->lb) + logfsboottrace(l->lb, l->logfstrace); + i = 0; + } + else if (i == 1 && strcmp(fields[0], "unconfig") == 0) { + if (l == nil) + error(Ebadarg); + if (l->ref.ref > 0) + error(Einuse); + devlogfsunconfig(l); + i = 0; + } + else if (i == 2 && strcmp(fields[0], "extent") == 0) { + if (l == nil) + error(Ebadarg); + devlogfsdumpinit(l, extentdumpinit, extentdumpread, i - 1, fields + 1); + i = 0; + } + else if (i >= 2 && strcmp(fields[0], "test") == 0) { + if (l == nil) + error(Ebadarg); + errorany(logfsservertestcmd(l->server, i - 1, fields + 1)); + i = 0; + } +#ifdef LEAKHUNT + else if (i == 1 && strcmp(fields[0], "leakaudit") == 0) { + leakaudit(); + i = 0; + } +#endif + } + if (i != 0) + error(Ebadarg); + return n; + } + error(Egreg); + return 0; /* not reached */ +} + +static void +devlogfsfree(Devlogfs *devlogfs) +{ + if (devlogfs != nil) { + int i; + logfsfreemem(devlogfs->device); + logfsfreemem(devlogfs->name); + for (i = 0; i < Qend - Qfs; i++) + logfsfreemem(devlogfs->filename[i]); + cclose(devlogfs->flash); + cclose(devlogfs->flashctl); + qlock(&devlogfs->qlock); + logfsserverfree(&devlogfs->server); + logfsbootfree(devlogfs->lb); + if (devlogfs->ll) + (*devlogfs->ll->free)(devlogfs->ll); + logfsfreemem(devlogfs->readbuf); + qunlock(&devlogfs->qlock); + logfsfreemem(devlogfs); + } +} + +#ifdef EMU +ulong +logfsnow(void) +{ + extern vlong timeoffset; + return (timeoffset + osusectime()) / 1000000; +} +#endif + +Dev logfsdevtab = { + 0x29f, +// L'ʟ', + "logfs", + +#ifndef EMU + devreset, +#endif + devinit, +#ifndef EMU + devshutdown, +#endif + devlogfsattach, + devlogfswalk, + devlogfsstat, + devlogfsopen, + devcreate, + devlogfsclose, + devlogfsread, + devbread, + devlogfswrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/emu/port/devmem.c b/emu/port/devmem.c new file mode 100644 index 00000000..6c09aaad --- /dev/null +++ b/emu/port/devmem.c @@ -0,0 +1,485 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "interp.h" + +enum +{ + Qdir, + Qctl, + Qstate, + Qsum, + Qevent, + Qprof, + Qheap, + Qgc +}; + +static +Dirtab memdir[] = +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "memctl", {Qctl}, 0, 0666, + "memstate", {Qstate}, 0, 0444, + "memsum", {Qsum}, 0, 0444, + "memevent", {Qevent}, 0, 0444, + "memprof", {Qprof}, 0, 0444, + "memheap", {Qheap}, 0, 0444, + "memgc", {Qgc}, 0, 0444, +}; + +enum +{ + /* these are the top two bits of size */ + Pflags= 3<<30, + Pfree= 0<<30, + Palloc= 1<<30, + Paend= 2<<30, + Pimmutable= 3<<30, + + Npool = 3, + Nevent = 10000, + Nstate = 12000 +}; + +/* + * pool snapshots + */ +typedef struct Pstate Pstate; +struct Pstate +{ + ulong base; + ulong size; +}; + +static struct +{ + Pstate state[3+Nstate]; + Pstate* lim; + Pstate* ptr; + int summary; +} poolstate[Npool]; +static Ref stateopen; + +/* + * pool/heap allocation events + */ +typedef struct Pevent Pevent; +struct Pevent +{ + int pool; + ulong pc; + ulong base; + ulong size; +}; + +static struct +{ + Lock l; + Ref inuse; + Rendez r; + int open; + Pevent events[Nevent]; + int rd; + int wr; + int full; + int want; + ulong lost; +} poolevents; + +/* + * allocation profiles + */ +typedef struct Pprof Pprof; +typedef struct Pbucket Pbucket; + +struct Pbucket +{ + ulong val; + ulong pool; + ulong count; + ulong size; + Pbucket* next; +}; + +static struct { + Ref inuse; /* only one of these things */ + Lock l; + Pbucket buckets[1000]; + Pbucket snap[1000]; + int used; + int snapped; + Pbucket* hash[128]; + ulong lost; +} memprof; + +extern void (*memmonitor)(int, ulong, ulong, ulong); +extern ulong gcnruns; +extern ulong gcsweeps; +extern ulong gcbroken; +extern ulong gchalted; +extern ulong gcepochs; +extern uvlong gcdestroys; +extern uvlong gcinspects; +extern uvlong gcbusy; +extern uvlong gcidle; +extern uvlong gcidlepass; +extern uvlong gcpartial; + + +static void +mprofreset(void) +{ + lock(&memprof.l); /* need ilock in kernel */ + memset(memprof.hash, 0, sizeof(memprof.hash)); + memprof.used = 0; + memprof.lost = 0; + unlock(&memprof.l); +} + +static void +mprofmonitor(int pool, ulong pc, ulong base, ulong size) +{ + Pbucket **h0, **h, *p; + + if((pool&7) == 1) + return; /* ignore heap */ + USED(base); + h0 = &memprof.hash[((pc>>16)^(pc>>4))&(nelem(memprof.hash)-1)]; + lock(&memprof.l); + for(h = h0; (p = *h) != nil; h = &p->next) + if(p->val == pc && p->pool == pool){ + p->count++; + p->size += size; + *h = p->next; + p->next = *h0; + *h0 = p; + unlock(&memprof.l); + return; + } + if(memprof.used >= nelem(memprof.buckets)){ + memprof.lost++; + unlock(&memprof.l); + return; + } + p = &memprof.buckets[memprof.used++]; + p->val = pc; + p->pool = pool; + p->count = 1; + p->size = size; + p->next = *h0; + *h0 = p; + unlock(&memprof.l); +} + +static void +_memmonitor(int pool, ulong pc, ulong base, ulong size) +{ + Pevent e; + + e.pool = pool; + e.pc = pc; + e.base = base; + e.size = size; + lock(&poolevents.l); + if(!poolevents.full){ + poolevents.events[poolevents.wr] = e; + if(++poolevents.wr == nelem(poolevents.events)) + poolevents.wr = 0; + if(poolevents.wr == poolevents.rd) + poolevents.full = 1; + }else + poolevents.lost++; + if(poolevents.want){ + poolevents.want = 0; + Wakeup(&poolevents.r); + } + unlock(&poolevents.l); +} + +static int +ismemdata(void *v) +{ + USED(v); + return poolevents.full || poolevents.rd != poolevents.wr; +} + +static char* +memaudit(int pno, Bhdr *b) +{ + Pstate *p; + + if(pno >= Npool) + return "too many pools for memaudit"; + if((p = poolstate[pno].ptr) == poolstate[pno].lim){ + if(b->magic == MAGIC_E) + return nil; + p = &poolstate[pno].state[1]; + if(b->magic == MAGIC_F) + p++; + p->base++; + p->size += b->size; + return nil; + } + poolstate[pno].ptr++; + p->base = (ulong)b; + p->size = b->size; + switch(b->magic){ + case MAGIC_A: + p->size |= Palloc; + break; + case MAGIC_F: + p->size |= Pfree; + break; + case MAGIC_E: + p->size = b->csize | Paend; + break; + case MAGIC_I: + p->size |= Pimmutable; + break; + default: + return "bad magic number in block"; + } + return nil; +} + +static void +mput4(uchar *m, ulong v) +{ + m[0] = v>>24; + m[1] = v>>16; + m[2] = v>>8; + m[3] = v; +} + +static Chan* +memattach(char *spec) +{ + return devattach('%', spec); +} + +static Walkqid* +memwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, memdir, nelem(memdir), devgen); +} + +static int +memstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, memdir, nelem(memdir), devgen); +} + +static Chan* +memopen(Chan *c, int omode) +{ + if(memmonitor != nil && c->qid.path != Qgc) + error(Einuse); + c = devopen(c, omode, memdir, nelem(memdir), devgen); + switch((ulong)c->qid.path){ + case Qevent: + if(incref(&poolevents.inuse) != 1){ + decref(&poolevents.inuse); + c->flag &= ~COPEN; + error(Einuse); + } + poolevents.rd = poolevents.wr = 0; + poolevents.full = 0; + poolevents.want = 0; + poolevents.lost = 0; + memmonitor = _memmonitor; + poolevents.open = 1; + break; + case Qstate: + if(incref(&stateopen) != 1){ + decref(&stateopen); + c->flag &= ~COPEN; + error(Einuse); + } + break; + case Qprof: + if(incref(&memprof.inuse) != 1){ + decref(&memprof.inuse); + c->flag &= ~COPEN; + error(Einuse); + } + memmonitor = mprofmonitor; + break; + } + return c; +} + +static void +memclose(Chan *c) +{ + if((c->flag & COPEN) == 0) + return; + switch((ulong)c->qid.path) { + case Qevent: + memmonitor = nil; + poolevents.open = 0; + decref(&poolevents.inuse); + break; + case Qstate: + decref(&stateopen); + break; + case Qprof: + decref(&memprof.inuse); + memmonitor = nil; + break; + } + +} + +static long +memread(Chan *c, void *va, long count, vlong offset) +{ + uchar *m; + char *e, *s; + int i, summary; + long n, nr; + Pstate *p; + Pevent pe; + Pbucket *b; + + if(c->qid.type & QTDIR) + return devdirread(c, va, count, memdir, nelem(memdir), devgen); + + summary = 0; + switch((ulong)c->qid.path) { + default: + error(Egreg); + case Qctl: + return 0; + case Qsum: + summary = 1; + /* fall through */ + case Qstate: + if(offset == 0){ + for(i=0; i<Npool; i++){ + poolstate[i].ptr = &poolstate[i].state[3]; + poolstate[i].lim = poolstate[i].ptr + Nstate; + memset(poolstate[i].state, 0, sizeof(poolstate[i].state)); + poolstate[i].summary = summary; + } + e = poolaudit(memaudit); + if(e != nil){ + print("mem: %s\n", e); + error(e); + } + } + m = va; + nr = offset/8; + for(i=0; i<Npool && count >= 8; i++){ + n = poolstate[i].ptr - poolstate[i].state; + poolstate[i].state[0].base = i; + poolstate[i].state[0].size = n; + if(nr >= n){ + nr -= n; + continue; + } + n -= nr; + p = &poolstate[i].state[nr]; + for(; --n >= 0 && (count -= 8) >= 0; m += 8, p++){ + mput4(m, p->base); + mput4(m+4, p->size); + } + } + return m-(uchar*)va; + case Qevent: + while(!ismemdata(nil)){ + poolevents.want = 1; + Sleep(&poolevents.r, ismemdata, nil); + } + m = va; + do{ + if((count -= 4*4) < 0) + return m-(uchar*)va; + pe = poolevents.events[poolevents.rd]; + mput4(m, pe.pool); + mput4(m+4, pe.pc); + mput4(m+8, pe.base); + mput4(m+12, pe.size); + m += 4*4; + if(++poolevents.rd >= nelem(poolevents.events)) + poolevents.rd = 0; + }while(poolevents.rd != poolevents.wr); + poolevents.full = 0; + return m-(uchar*)va; + case Qprof: + if(offset == 0){ + lock(&memprof.l); + memmove(memprof.snap, memprof.buckets, memprof.used*sizeof(memprof.buckets[0])); + memprof.snapped = memprof.used; + unlock(&memprof.l); + } + m = va; + for(i = offset/(4*4); i < memprof.snapped && (count -= 4*4) >= 0; i++){ + b = &memprof.snap[i]; + mput4(m, b->pool); + mput4(m+4, b->val); + mput4(m+8, b->count); + mput4(m+12, b->size); + m += 4*4; + } + return m-(uchar*)va; + case Qgc: + s = malloc(READSTR); + if(s == nil) + error(Enomem); + if(waserror()){ + free(s); + nexterror(); + } + snprint(s, READSTR, "runs: %lud\nsweeps: %lud\nbchain: %lud\nhalted: %lud\nepochs: %lud\ndestroy: %llud\ninspects: %llud\nbusy: %llud\nidle: %llud\nidlepass: %llud\npartial: %llud\n", + gcnruns, gcsweeps, gcbroken, gchalted, gcepochs, gcdestroys, gcinspects, gcbusy, gcidle, gcidlepass, gcpartial); + count = readstr(offset, va, count, s); + poperror(); + free(s); + return count; + } + return 0; +} + +static long +memwrite(Chan *c, void *va, long count, vlong offset) +{ + USED(offset); + USED(count); + USED(va); + + if(c->qid.type & QTDIR) + error(Eperm); + + switch((ulong)c->qid.path) { + default: + error(Egreg); + case Qctl: + error(Ebadarg); + case Qstate: + error(Eperm); + case Qprof: + mprofreset(); + break; + } + return 0; +} + +Dev memdevtab = { + '%', + "mem", + + devinit, + memattach, + memwalk, + memstat, + memopen, + devcreate, + memclose, + memread, + devbread, + memwrite, + devbwrite, + devremove, + devwstat +}; diff --git a/emu/port/devmnt.c b/emu/port/devmnt.c new file mode 100644 index 00000000..0d490625 --- /dev/null +++ b/emu/port/devmnt.c @@ -0,0 +1,1202 @@ +#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 l; + 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 +mntinit(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); */ +/* fmtinstall('M', dirmodefmt); */ + + 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->l); + oo = c->offset; + c->offset += k; + unlock(&c->l); + + l = devtab[c->type]->write(c, msg, k, oo); + + if(l < k){ + lock(&c->l); + c->offset -= k - l; + unlock(&c->l); + 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->l); + c->offset += k; + unlock(&c->l); + + 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"); + if(strncmp(f.version, v, strlen(f.version)) != 0) + error("bad 9P version returned from server"); + + /* now build Mnt associated with this connection */ + lock(&mntalloc.l); + m = mntalloc.mntfree; + if(m != 0) + mntalloc.mntfree = m->list; + else { + m = malloc(sizeof(Mnt)); + if(m == 0) { + unlock(&mntalloc.l); + 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, nil, nil); + m->msize = f.msize; + unlock(&mntalloc.l); + + poperror(); /* msg */ + free(msg); + + lock(&m->l); + m->queue = 0; + m->rip = 0; + + c->flag |= CMSG; + c->mux = m; + m->c = c; + unlock(&m->l); + + poperror(); /* c */ + qunlock(&c->umqlock); + + k = strlen(f.version); + if(returnlen > 0){ + if(returnlen < k) + error(Eshort); + memmove(version, f.version, k); + } + + 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->env->user; + r->request.aname = spec; + mountrpc(m, r); + + c->qid = r->reply.aqid; + c->mchan = m->c; + incref(&m->c->r); + 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->env->user; + r->request.aname = bogus.spec; + mountrpc(m, r); + + c->qid = r->reply.qid; + c->mchan = m->c; + incref(&m->c->r); + 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.l); + c->dev = mntalloc.id++; + unlock(&mntalloc.l); + + 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->r); + } + 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); + + if(r->reply.nstat > n){ + /* 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.l); + 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.l); + + 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; /* TO DO: volatile struct { Mntrpc *r; } 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 || up->killed) + 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 %s %lud: mismatch from %s %s rep 0x%p tag %d fid %d T%d R%d rp %d\n", + up->text, 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->env->errstr, Eintr) != 0){ + mntflushfree(m, r); + nexterror(); + } + r = mntflushalloc(r, m->msize); + } + + lock(&m->l); + r->m = m; + r->list = m->queue; + m->queue = r; + unlock(&m->l); + + /* 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->l); + if(m->rip == 0) + break; + unlock(&m->l); + Sleep(&r->r, rpcattn, r); + if(r->done){ + poperror(); + mntflushfree(m, r); + return; + } + } + m->rip = up; + unlock(&m->l); + 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(blocklen(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 ridiculous (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->l); + m->rip = 0; + for(q = m->queue; q; q = q->list) { + if(q->done == 0) + if(Wakeup(&q->r)) + break; + } + unlock(&m->l); +} + +void +mountmux(Mnt *m, Mntrpc *r) +{ + Mntrpc **l, *q; + + lock(&m->l); + 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->l); + if(mntstats != nil) + (*mntstats)(q->request.type, + m->c, q->stime, + q->reqlen + r->replen); + if(q != r) + Wakeup(&q->r); + return; + } + l = &q->list; + } + unlock(&m->l); + if(r->reply.type == Rerror) + print("unexpected reply tag %ud; type %d (error %q)\n", r->reply.tag, r->reply.type, r->reply.ename); + else + 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; + } +} + +static int +alloctag(void) +{ + int i, j; + ulong v; + + for(i = 0; i < NMASK; i++){ + v = mntalloc.tagmask[i]; + if(v == ~0UL) + continue; + for(j = 0; j < 1<<TAGSHIFT; j++) + if((v & (1<<j)) == 0){ + mntalloc.tagmask[i] |= 1<<j; + return (i<<TAGSHIFT) + j; + } + } + /* panic("no devmnt tags left"); */ + return NOTAG; +} + +static void +freetag(int t) +{ + mntalloc.tagmask[t>>TAGSHIFT] &= ~(1<<(t&TAGMASK)); +} + +Mntrpc* +mntralloc(Chan *c, ulong msize) +{ + Mntrpc *new; + + lock(&mntalloc.l); + new = mntalloc.rpcfree; + if(new == nil){ + new = malloc(sizeof(Mntrpc)); + if(new == nil) { + unlock(&mntalloc.l); + 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.l); + exhausted("mount rpc buffer"); + } + new->rpclen = msize; + new->request.tag = alloctag(); + if(new->request.tag == NOTAG){ + free(new); + unlock(&mntalloc.l); + exhausted("rpc tags"); + } + } + 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.l); + exhausted("mount rpc buffer"); + } + new->rpclen = msize; + } + } + mntalloc.nrpcused++; + unlock(&mntalloc.l); + 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.l); + 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.l); +} + +void +mntqrm(Mnt *m, Mntrpc *r) +{ + Mntrpc **l, *f; + + lock(&m->l); + 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->l); +} + +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", + + mntinit, + mntattach, + mntwalk, + mntstat, + mntopen, + mntcreate, + mntclose, + mntread, + devbread, + mntwrite, + devbwrite, + mntremove, + mntwstat, +}; diff --git a/emu/port/devpipe.c b/emu/port/devpipe.c new file mode 100644 index 00000000..32c16ab6 --- /dev/null +++ b/emu/port/devpipe.c @@ -0,0 +1,460 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include <interp.h> + +#define NETTYPE(x) ((ulong)(x)&0x1f) +#define NETID(x) (((ulong)(x))>>5) +#define NETQID(i,t) (((i)<<5)|(t)) + +typedef struct Pipe Pipe; +struct Pipe +{ + QLock l; + Pipe* next; + int ref; + ulong path; + Queue* q[2]; + int qref[2]; + Dirtab* pipedir; + char* user; +}; + +static struct +{ + Lock l; + ulong path; + int pipeqsize; +} pipealloc; + +enum +{ + Qdir, + Qdata0, + Qdata1 +}; + +Dirtab pipedir[] = +{ + ".", {Qdir,0,QTDIR}, 0, DMDIR|0500, + "data", {Qdata0}, 0, 0660, + "data1", {Qdata1}, 0, 0660, +}; + +static void +freepipe(Pipe *p) +{ + if(p != nil){ + free(p->user); + free(p->q[0]); + free(p->q[1]); + free(p->pipedir); + free(p); + } +} + +static void +pipeinit(void) +{ + pipealloc.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) + error(Enomem); + if(waserror()){ + freepipe(p); + nexterror(); + } + p->pipedir = malloc(sizeof(pipedir)); + if (p->pipedir == 0) + error(Enomem); + memmove(p->pipedir, pipedir, sizeof(pipedir)); + kstrdup(&p->user, up->env->user); + p->ref = 1; + + p->q[0] = qopen(pipealloc.pipeqsize, 0, 0, 0); + if(p->q[0] == 0) + error(Enomem); + p->q[1] = qopen(pipealloc.pipeqsize, 0, 0, 0); + if(p->q[1] == 0) + error(Enomem); + poperror(); + + lock(&pipealloc.l); + p->path = ++pipealloc.path; + unlock(&pipealloc.l); + + c->qid.path = NETQID(2*p->path, Qdir); + c->qid.vers = 0; + c->qid.type = QTDIR; + c->aux = p; + c->dev = 0; + return c; +} + +static int +pipegen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp) +{ + int id, len; + Qid qid; + Pipe *p; + + USED(name); + if(i == DEVDOTDOT){ + devdir(c, c->qid, "#|", 0, eve, 0555, dp); + return 1; + } + i++; /* skip . */ + if(tab==0 || i>=ntab) + return -1; + tab += i; + p = c->aux; + switch(NETTYPE(tab->qid.path)){ + case Qdata0: + len = qlen(p->q[0]); + break; + case Qdata1: + len = qlen(p->q[1]); + break; + default: + len = tab->length; + break; + } + id = NETID(c->qid.path); + qid.path = NETQID(id, tab->qid.path); + qid.vers = 0; + qid.type = QTFILE; + devdir(c, qid, tab->name, len, eve, tab->perm, dp); + return 1; +} + +static Walkqid* +pipewalk(Chan *c, Chan *nc, char **name, int nname) +{ + Walkqid *wq; + Pipe *p; + + p = c->aux; + wq = devwalk(c, nc, name, nname, p->pipedir, nelem(pipedir), pipegen); + if(wq != nil && wq->clone != nil && wq->clone != c){ + qlock(&p->l); + p->ref++; + if(c->flag & COPEN){ + switch(NETTYPE(c->qid.path)){ + case Qdata0: + p->qref[0]++; + break; + case Qdata1: + p->qref[1]++; + break; + } + } + qunlock(&p->l); + } + return wq; +} + +static int +pipestat(Chan *c, uchar *db, int n) +{ + Pipe *p; + Dir dir; + Dirtab *tab; + + p = c->aux; + tab = p->pipedir; + + switch(NETTYPE(c->qid.path)){ + case Qdir: + devdir(c, c->qid, ".", 0, eve, DMDIR|0555, &dir); + break; + case Qdata0: + devdir(c, c->qid, tab[1].name, qlen(p->q[0]), eve, tab[1].perm, &dir); + break; + case Qdata1: + devdir(c, c->qid, tab[2].name, qlen(p->q[1]), eve, tab[2].perm, &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; + } + + openmode(omode); /* check it */ + + p = c->aux; + qlock(&p->l); + if(waserror()){ + qunlock(&p->l); + nexterror(); + } + switch(NETTYPE(c->qid.path)){ + case Qdata0: + devpermcheck(p->user, p->pipedir[1].perm, omode); + p->qref[0]++; + break; + case Qdata1: + devpermcheck(p->user, p->pipedir[2].perm, omode); + p->qref[1]++; + break; + } + poperror(); + qunlock(&p->l); + + 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->l); + + 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->l); + freepipe(p); + } else + qunlock(&p->l); +} + +static long +piperead(Chan *c, void *va, long n, vlong junk) +{ + Pipe *p; + + p = c->aux; + + USED(junk); + switch(NETTYPE(c->qid.path)){ + case Qdir: + return devdirread(c, va, n, p->pipedir, nelem(pipedir), 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 an exception to be sent to + * the prog. + */ +static long +pipewrite(Chan *c, void *va, long n, vlong junk) +{ + Pipe *p; + Prog *r; + + USED(junk); + if(waserror()) { + /* avoid exceptions when pipe is a mounted queue */ + if((c->flag & CMSG) == 0) { + r = up->iprog; + if(r != nil && r->kill == nil) + r->kill = "write on closed pipe"; + } + 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 junk) +{ + long n; + Pipe *p; + Prog *r; + + USED(junk); + if(waserror()) { + /* avoid exceptions when pipe is a mounted queue */ + if((c->flag & CMSG) == 0) { + r = up->iprog; + if(r != nil && r->kill == nil) + r->kill = "write on closed pipe"; + } + 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; +} + +static int +pipewstat(Chan *c, uchar *dp, int n) +{ + Dir *d; + Pipe *p; + int d1; + + if (c->qid.type&QTDIR) + error(Eperm); + p = c->aux; + if(strcmp(up->env->user, p->user) != 0) + error(Eperm); + d = smalloc(sizeof(*d)+n); + if(waserror()){ + free(d); + nexterror(); + } + n = convM2D(dp, n, d, (char*)&d[1]); + if(n == 0) + error(Eshortstat); + d1 = NETTYPE(c->qid.path) == Qdata1; + if(!emptystr(d->name)){ + validwstatname(d->name); + if(strlen(d->name) >= KNAMELEN) + error(Efilename); + if(strcmp(p->pipedir[1+!d1].name, d->name) == 0) + error(Eexist); + kstrcpy(p->pipedir[1+d1].name, d->name, KNAMELEN); + } + if(d->mode != ~0UL) + p->pipedir[d1 + 1].perm = d->mode & 0777; + poperror(); + free(d); + return n; +} + +Dev pipedevtab = { + '|', + "pipe", + + pipeinit, + pipeattach, + pipewalk, + pipestat, + pipeopen, + devcreate, + pipeclose, + piperead, + pipebread, + pipewrite, + pipebwrite, + devremove, + pipewstat, +}; diff --git a/emu/port/devpointer.c b/emu/port/devpointer.c new file mode 100644 index 00000000..30bad028 --- /dev/null +++ b/emu/port/devpointer.c @@ -0,0 +1,304 @@ +/* + * mouse or stylus + */ + +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> + +#define cursorenable() +#define cursordisable() + +enum{ + Qdir, + Qpointer, + Qcursor +}; + +typedef struct Pointer Pointer; + +struct Pointer { + int x; + int y; + int b; + ulong msec; +}; + +static struct +{ + Pointer v; + int modify; + int lastb; + Rendez r; + Ref ref; + QLock q; +} mouse; + +static +Dirtab pointertab[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "pointer", {Qpointer}, 0, 0666, + "cursor", {Qcursor}, 0, 0222, +}; + +enum { + Nevent = 16 /* enough for some */ +}; + +static struct { + int rd; + int wr; + Pointer clicks[Nevent]; + Rendez r; + int full; + int put; + int get; +} ptrq; + +/* + * called by any source of pointer data + */ +void +mousetrack(int b, int x, int y, int isdelta) +{ + int lastb; + ulong msec; + Pointer e; + + if(isdelta){ + x += mouse.v.x; + y += mouse.v.y; + } + msec = osmillisec(); + if(0 && b && (mouse.v.b ^ b)&0x1f){ + if(msec - mouse.v.msec < 300 && mouse.lastb == b + && abs(mouse.v.x - x) < 12 && abs(mouse.v.y - y) < 12) + b |= 1<<8; + mouse.lastb = b & 0x1f; + mouse.v.msec = msec; + } + if((b&(1<<8))==0 && x == mouse.v.x && y == mouse.v.y && mouse.v.b == b) + return; + lastb = mouse.v.b; + mouse.v.x = x; + mouse.v.y = y; + mouse.v.b = b; + mouse.v.msec = msec; + if(!ptrq.full && lastb != b){ + e = mouse.v; + ptrq.clicks[ptrq.wr] = e; + if(++ptrq.wr >= Nevent) + ptrq.wr = 0; + if(ptrq.wr == ptrq.rd) + ptrq.full = 1; + } + mouse.modify = 1; + ptrq.put++; + Wakeup(&ptrq.r); +/* drawactive(1); */ +/* setpointer(x, y); */ +} + +static int +ptrqnotempty(void *x) +{ + USED(x); + return ptrq.full || ptrq.put != ptrq.get; +} + +static Pointer +mouseconsume(void) +{ + Pointer e; + + Sleep(&ptrq.r, ptrqnotempty, 0); + ptrq.full = 0; + ptrq.get = ptrq.put; + if(ptrq.rd != ptrq.wr){ + e = ptrq.clicks[ptrq.rd]; + if(++ptrq.rd >= Nevent) + ptrq.rd = 0; + }else + e = mouse.v; + return e; +} + +Point +mousexy(void) +{ + return Pt(mouse.v.x, mouse.v.y); +} + + +static Chan* +pointerattach(char* spec) +{ + return devattach('m', spec); +} + +static Walkqid* +pointerwalk(Chan *c, Chan *nc, char **name, int nname) +{ + Walkqid *wq; + + wq = devwalk(c, nc, name, nname, pointertab, nelem(pointertab), devgen); + if(wq != nil && wq->clone != c && wq->clone != nil && (ulong)c->qid.path == Qpointer) + incref(&mouse.ref); /* can this happen? */ + return wq; +} + +static int +pointerstat(Chan* c, uchar *db, int n) +{ + return devstat(c, db, n, pointertab, nelem(pointertab), devgen); +} + +static Chan* +pointeropen(Chan* c, int omode) +{ + c = devopen(c, omode, pointertab, nelem(pointertab), devgen); + if((ulong)c->qid.path == Qpointer){ + if(waserror()){ + c->flag &= ~COPEN; + nexterror(); + } + if(!canqlock(&mouse.q)) + error(Einuse); + if(incref(&mouse.ref) != 1){ + qunlock(&mouse.q); + error(Einuse); + } + cursorenable(); + qunlock(&mouse.q); + poperror(); + } + return c; +} + +static void +pointerclose(Chan* c) +{ + if((c->flag & COPEN) == 0) + return; + switch((ulong)c->qid.path){ + case Qpointer: + qlock(&mouse.q); + if(decref(&mouse.ref) == 0){ + cursordisable(); + } + qunlock(&mouse.q); + break; + } +} + +static long +pointerread(Chan* c, void* a, long n, vlong off) +{ + Pointer mt; + char buf[1+4*12+1]; + int l; + + USED(&off); + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, pointertab, nelem(pointertab), devgen); + case Qpointer: + qlock(&mouse.q); + if(waserror()) { + qunlock(&mouse.q); + nexterror(); + } + mt = mouseconsume(); + poperror(); + qunlock(&mouse.q); + l = snprint(buf, sizeof(buf), "m%11d %11d %11d %11lud ", mt.x, mt.y, mt.b, mt.msec); + if(l < n) + n = l; + memmove(a, buf, n); + break; + default: + n=0; + break; + } + return n; +} + +static long +pointerwrite(Chan* c, void* va, long n, vlong off) +{ + char *a = va; + char buf[128]; + int b, x, y; + Drawcursor cur; + + USED(&off); + switch((ulong)c->qid.path){ + case Qpointer: + if(n > sizeof buf-1) + n = sizeof buf -1; + memmove(buf, va, n); + buf[n] = 0; + x = strtoul(buf+1, &a, 0); + if(*a == 0) + error(Eshort); + y = strtoul(a, &a, 0); + if(*a != 0) + b = strtoul(a, 0, 0); + else + b = mouse.v.b; + /*mousetrack(b, x, y, msec);*/ + setpointer(x, y); + USED(b); + break; + case Qcursor: + /* TO DO: perhaps interpret data as an Image */ + /* + * hotx[4] hoty[4] dx[4] dy[4] clr[dx/8 * dy/2] set[dx/8 * dy/2] + * dx must be a multiple of 8; dy must be a multiple of 2. + */ + if(n == 0){ + cur.data = nil; + drawcursor(&cur); + break; + } + if(n < 8) + error(Eshort); + cur.hotx = BGLONG((uchar*)va+0*4); + cur.hoty = BGLONG((uchar*)va+1*4); + cur.minx = 0; + cur.miny = 0; + cur.maxx = BGLONG((uchar*)va+2*4); + cur.maxy = BGLONG((uchar*)va+3*4); + if(cur.maxx%8 != 0 || cur.maxy%2 != 0 || n-4*4 != (cur.maxx/8 * cur.maxy)) + error(Ebadarg); + cur.data = (uchar*)va + 4*4; + drawcursor(&cur); + break; + default: + error(Ebadusefd); + } + return n; +} + +Dev pointerdevtab = { + 'm', + "pointer", + + devinit, + pointerattach, + pointerwalk, + pointerstat, + pointeropen, + devcreate, + pointerclose, + pointerread, + devbread, + pointerwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/emu/port/devprof.c b/emu/port/devprof.c new file mode 100644 index 00000000..f3a768c3 --- /dev/null +++ b/emu/port/devprof.c @@ -0,0 +1,831 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "interp.h" +#include <isa.h> +#include "runt.h" + +extern Pool* imagmem; +extern void (*memmonitor)(int, ulong, ulong, ulong); + +static void cpxec(Prog *); +static void memprof(int, void*, ulong); +static void memprofmi(int, ulong, ulong, ulong); + +extern Inst* pc2dispc(Inst*, Module*); + +static int interval = 100; /* Sampling interval in milliseconds */ + +enum +{ + HSIZE = 32, +}; + +#define HASH(m) ((m)%HSIZE) + +/* cope with multiple profilers some day */ + +typedef struct Record Record; +struct Record +{ + int id; + char* name; + char* path; + Inst* base; + int size; + /*Module* m; */ + ulong mtime; + Qid qid; + Record* hash; + Record* link; + ulong bucket[1]; +}; + +struct +{ + Lock l; + vlong time; + Record* hash[HSIZE]; + Record* list; +} profile; + +typedef struct Pmod Pmod; +struct Pmod +{ + char* name; + Pmod* link; +} *pmods; + +#define QSHIFT 4 +#define QID(q) ((ulong)(q).path&0xf) +#define QPID(pid) ((pid)<<QSHIFT) +#define PID(q) ((q).vers) +#define PATH(q) ((ulong)(q).path&~((1<<QSHIFT)-1)) + +enum +{ + Qdir, + Qname, + Qpath, + Qhist, + Qpctl, + Qctl, +}; + +Dirtab profdir[] = +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "name", {Qname}, 0, 0444, + "path", {Qpath}, 0, 0444, + "histogram", {Qhist}, 0, 0444, + "pctl", {Qpctl}, 0, 0222, + "ctl", {Qctl}, 0, 0222, +}; + +enum{ + Pnil, /* null profiler */ + Psam, /* sampling profiler */ + Pcov, /* coverage profiler */ + Pmem, /* heap memory profiler */ +}; + +enum{ + Mnone = 0, + Mmain = 1, + Mheap = 2, + Mimage = 4, +}; + +static int profiler = Pnil; +static int mprofiler = Mnone; + +static int ids; +static int samplefn; + +static void sampler(void*); + +static Record* +getrec(int id) +{ + Record *r; + + for(r = profile.list; r != nil; r = r->link) + if(r->id == id) + break; + return r; +} + +static void +addpmod(char *m) +{ + Pmod *p = malloc(sizeof(Pmod)); + + if(p == nil) + return; + p->name = malloc(strlen(m)+1); + if(p->name == nil){ + free(p); + return; + } + strcpy(p->name, m); + p->link = pmods; + pmods = p; +} + +static void +freepmods(void) +{ + Pmod *p, *np; + + for(p = pmods; p != nil; p = np){ + free(p->name); + np = p->link; + free(p); + } + pmods = nil; +} + +static int +inpmods(char *m) +{ + Pmod *p; + + for(p = pmods; p != nil; p = p->link) + if(strcmp(p->name, m) == 0) + return 1; + return 0; +} + +static void +freeprof(void) +{ + int i; + Record *r, *nr; + + ids = 0; + profiler = Pnil; + mprofiler = Mnone; + freepmods(); + for(r = profile.list; r != nil; r = nr){ + free(r->name); + free(r->path); + nr = r->link; + free(r); + } + profile.list = nil; + profile.time = 0; + for(i = 0; i < HSIZE; i++) + profile.hash[i] = nil; +} + +static int +profgen(Chan *c, char *name, Dirtab *d, int nd, int s, Dir *dp) +{ + Qid qid; + Record *r; + ulong path, perm, len; + Dirtab *tab; + + USED(name); + USED(d); + USED(nd); + + if(s == DEVDOTDOT) { + mkqid(&qid, Qdir, 0, QTDIR); + devdir(c, qid, "#P", 0, eve, 0555, dp); + return 1; + } + + if(c->qid.path == Qdir && c->qid.type & QTDIR) { + acquire(); + if(s-- == 0){ + tab = &profdir[Qctl]; + mkqid(&qid, PATH(c->qid)|tab->qid.path, c->qid.vers, QTFILE); + devdir(c, qid, tab->name, tab->length, eve, tab->perm, dp); + release(); + return 1; + } + r = profile.list; + while(s-- && r != nil) + r = r->link; + if(r == nil) { + release(); + return -1; + } + sprint(up->genbuf, "%.8lux", (ulong)r->id); + mkqid(&qid, (r->id<<QSHIFT), r->id, QTDIR); + devdir(c, qid, up->genbuf, 0, eve, DMDIR|0555, dp); + release(); + return 1; + } + if(s >= nelem(profdir)-1) + error(Enonexist); /* was return -1; */ + tab = &profdir[s]; + path = PATH(c->qid); + + acquire(); + r = getrec(PID(c->qid)); + if(r == nil) { + release(); + error(Enonexist); /* was return -1; */ + } + + perm = tab->perm; + len = tab->length; + mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE); + devdir(c, qid, tab->name, len, eve, perm, dp); + release(); + return 1; +} + +static Chan* +profattach(char *spec) +{ + return devattach('P', spec); +} + +static Walkqid* +profwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, profgen); +} + +static int +profstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, profgen); +} + +static Chan* +profopen(Chan *c, int omode) +{ + int qid; + Record *r; + + if(c->qid.type & QTDIR) { + if(omode != OREAD) + error(Eisdir); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; + } + + if(omode&OTRUNC) + error(Eperm); + + qid = QID(c->qid); + if(qid == Qctl || qid == Qpctl){ + if (omode != OWRITE) + error(Eperm); + } + else{ + if(omode != OREAD) + error(Eperm); + } + + if(qid != Qctl){ + acquire(); + r = getrec(PID(c->qid)); + release(); + if(r == nil) + error(Ethread); + } + + c->offset = 0; + c->flag |= COPEN; + c->mode = openmode(omode); + if(QID(c->qid) == Qhist) + c->aux = nil; + return c; +} + +static int +profwstat(Chan *c, uchar *dp, int n) +{ + Dir d; + Record *r; + + if(strcmp(up->env->user, eve)) + error(Eperm); + if(c->qid.type & QTDIR) + error(Eperm); + acquire(); + r = getrec(PID(c->qid)); + release(); + if(r == nil) + error(Ethread); + n = convM2D(dp, n, &d, nil); + if(n == 0) + error(Eshortstat); + d.mode &= 0777; + /* TO DO: copy to c->aux->perm, once that exists */ + return n; +} + +static void +profclose(Chan *c) +{ + USED(c); +} + +static long +profread(Chan *c, void *va, long n, vlong offset) +{ + int i; + Record *r; + char *a = va; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, profgen); + acquire(); + r = getrec(PID(c->qid)); + release(); + if(r == nil) + error(Ethread); + switch(QID(c->qid)){ + case Qname: + return readstr(offset, va, n, r->name); + case Qpath: + return readstr(offset, va, n, r->path); + case Qhist: + i = (int)c->aux; + while(i < r->size && r->bucket[i] == 0) + i++; + if(i >= r->size) + return 0; + c->aux = (void*)(i+1); + if(n < 20) + error(Etoosmall); + return sprint(a, "%d %lud", i, r->bucket[i]); + case Qctl: + error(Eperm); + } + return 0; +} + +static long +profwrite(Chan *c, void *va, long n, vlong offset) +{ + int i; + char *a = va; + char buf[128], *fields[128]; + void (*f)(int, ulong, ulong, ulong); + + USED(va); + USED(n); + USED(offset); + + if(c->qid.type & QTDIR) + error(Eisdir); + switch(QID(c->qid)){ + case Qctl: + if(n > sizeof(buf)-1) + n = sizeof(buf)-1; + memmove(buf, a, n); + buf[n] = 0; + i = getfields(buf, fields, nelem(fields), 1, " \t\n"); + if(i > 0 && strcmp(fields[0], "module") == 0){ + f = memmonitor; + memmonitor = nil; + freepmods(); + while(--i > 0) + addpmod(fields[i]); + memmonitor = f; + return n; + } + if(i == 1){ + if(strcmp(fields[0], "start") == 0){ + if(profiler == Pnil) { + profiler = Psam; + if(!samplefn){ + samplefn = 1; + kproc("prof", sampler, 0, 0); + } + } + } + else if(strncmp(fields[0], "startmp", 7) == 0){ + if(profiler == Pnil){ + profiler = Pmem; + for(a = &fields[0][7]; *a != '\0'; a++){ + if(*a == '1'){ + memmonitor = memprofmi; + mprofiler |= Mmain; + } + else if(*a == '2'){ + heapmonitor = memprof; + mprofiler |= Mheap; + } + else if(*a == '3'){ + memmonitor = memprofmi; + mprofiler |= Mimage; + } + }; + } + } + else if(strcmp(fields[0], "stop") == 0){ + profiler = Pnil; + mprofiler = Mnone; + } + else if(strcmp(fields[0], "end") == 0){ + profiler = Pnil; + mprofiler = Mnone; + memmonitor = nil; + freeprof(); + interval = 100; + } + else + error(Ebadarg); + } + else if (i == 2){ + if(strcmp(fields[0], "interval") == 0) + interval = strtoul(fields[1], nil, 0); + else if(strcmp(fields[0], "startcp") == 0){ + Prog *p; + + acquire(); + p = progpid(strtoul(fields[1], nil, 0)); + if(p == nil){ + release(); + return -1; + } + if(profiler == Pnil){ + profiler = Pcov; + p->xec = cpxec; + } + release(); + } + else + error(Ebadarg); + } + else + error(Ebadarg); + return n; + default: + error(Eperm); + } + return 0; +} + +static Record* +newmodule(Module *m, int vm, int scale, int origin) +{ + int dsize; + Record *r, **l; + + if(!vm) + acquire(); + if((m->compiled && m->pctab == nil) || m->prog == nil) { + if(!vm) + release(); + return nil; + } + if(m->compiled) + dsize = m->nprog * sizeof(r->bucket[0]); + else + dsize = (msize(m->prog)/sizeof(Inst)) * sizeof(r->bucket[0]); + dsize *= scale; + dsize += origin; + r = malloc(sizeof(Record)+dsize); + if(r == nil) { + if(!vm) + release(); + return nil; + } + + r->id = ++ids; + if(ids == (1<<8)-1) + ids = 0; + kstrdup(&r->name, m->name); + kstrdup(&r->path, m->path); + r->base = m->prog; + r->size = dsize/sizeof(r->bucket[0]); + /* r->m = m; */ + r->mtime = m->mtime; + r->qid.path = m->qid.path; + r->qid.vers = m->qid.vers; + memset(r->bucket, 0, dsize); + r->link = profile.list; + profile.list = r; + + l = &profile.hash[HASH(m->mtime)]; + r->hash = *l; + *l = r; + + if(!vm) + release(); + return r; +} + +#define LIMBO(m) ((m)->path[0] != '$') + +Module* +limbomodule(void) +{ + Frame *f; + uchar *fp; + Module *m; + + m = R.M->m; + if(LIMBO(m)) + return m; + for(fp = R.FP ; fp != nil; fp = f->fp){ + f = (Frame*)fp; + if(f->mr != nil){ + m = f->mr->m; + if(LIMBO(m)) + return m; + } + } + return nil; +} + +static Record* +mlook(Module *m, int limbo, int vm, int scale, int origin) +{ + Record *r; + void (*f)(int, ulong, ulong, ulong); + + if(limbo) + m = limbomodule(); + if(m == nil) + return nil; + for(r = profile.hash[HASH(m->mtime)]; r; r = r->hash){ + if(r->mtime == m->mtime && r->qid.path == m->qid.path && r->qid.vers == m->qid.vers && strcmp(r->name, m->name) == 0 && strcmp(r->path, m->path) == 0){ + r->base = m->prog; + return r; + } + } + if(pmods == nil || inpmods(m->name) || inpmods(m->path)){ + f = memmonitor; + memmonitor = nil; /* prevent monitoring of our memory usage */ + r = newmodule(m, vm, scale, origin); + memmonitor = f; + return r; + } + return nil; +} + +static void +sampler(void* a) +{ + int i; + Module *m; + Record *r; + Inst *p; + + USED(a); + for(;;) { + osmillisleep(interval); + if(profiler != Psam) + break; + lock(&profile.l); + profile.time += interval; + if(R.M == H || (m = R.M->m) == nil){ + unlock(&profile.l); + continue; + } + p = R.PC; + r = mlook(m, 0, 0, 1, 0); + if(r == nil){ + unlock(&profile.l); + continue; + } + if(m->compiled && m->pctab != nil) + p = pc2dispc(p, m); + if((i = p-r->base) >= 0 && i < r->size) + r->bucket[i]++; + unlock(&profile.l); + } + samplefn = 0; + pexit("", 0); +} + +/* + * coverage profiling + */ + +static void +cpxec(Prog *p) +{ + int op, i; + Module *m; + Record *r; + Prog *n; + + R = p->R; + R.MP = R.M->MP; + R.IC = p->quanta; + + if(p->kill != nil){ + char *m; + m = p->kill; + p->kill = nil; + error(m); + } + + if(R.M->compiled) + comvec(); + else{ + m = R.M->m; + r = profiler == Pcov ? mlook(m, 0, 1, 1, 0) : nil; + do{ + dec[R.PC->add](); + op = R.PC->op; + if(r != nil){ + i = R.PC-r->base; + if(i >= 0 && i < r->size) + r->bucket[i]++; + } + R.PC++; + optab[op](); + if(op == ISPAWN || op == IMSPAWN){ + n = delruntail(Pdebug); /* any state will do */ + n->xec = cpxec; + addrun(n); + } + if(m != R.M->m){ + m = R.M->m; + r = profiler == Pcov ? mlook(m, 0, 1, 1, 0) : nil; + } + }while(--R.IC != 0); + } + + p->R = R; +} + +/* memory profiling */ + +enum{ + Mhalloc, + Mhfree, + Mgcfree, + Mmfree, + Mmalloc, + Mifree, + Mialloc, +}; + +#ifdef HEAP_ALIGN + +static void +memprof(int c, void *v, ulong n) +{ + int i, j, k; + ulong kk, *b; + Module *m; + Record *r; + Inst *p; + Heap *h; + + USED(v); + USED(n); + if(profiler != Pmem){ + memmonitor = nil; + heapmonitor = nil; + return; + } + lock(&profile.l); + m = nil; + if(c != Mgcfree && (R.M == H || (m = R.M->m) == nil)){ + unlock(&profile.l); + return; + } + h = v; + if(c == Mhalloc || c == Mmalloc || c == Mialloc){ + p = R.PC; + if(m->compiled && m->pctab != nil) + p = pc2dispc(p, m); + if((r = mlook(m, 1, 1, 2, 2)) == nil){ + unlock(&profile.l); + return; + } + i = p-r->base; + k = (r->id<<24) | i; + if(c == Mhalloc){ + h->pad = k; + j = hmsize(h)-sizeof(Heap); + } + else if(c == Mmalloc){ + setmalloctag(v, k); + j = msize(v); + } + else{ + ((ulong*)v)[1] = k; + j = poolmsize(imagmem, v)-sizeof(ulong); + } + } + else{ + if(c == Mmfree) + k = getmalloctag(v); + else if(c == Mifree) + k = ((ulong*)v)[1]; + else + k = h->pad; + if((r = getrec(k>>24)) == nil){ + unlock(&profile.l); + return; + } + i = k&0xffffff; + if(c == Mmfree) + j = msize(v); + else if(c == Mifree) + j = poolmsize(imagmem, v)-sizeof(ulong); + else + j = hmsize(h)-sizeof(Heap); + j = -j; + } + i = 2*(i+1); + b = r->bucket; + if(i >= 0 && i < r->size){ + if(0){ + if(c == 1){ + b[0] -= j; + b[i] -= j; + } + else if(c == 2){ + b[1] -= j; + b[i+1] -= j; + } + } + else{ + b[0] += j; + if((int)b[0] < 0) + b[0] = 0; + b[i] += j; + if((int)b[i] < 0) + b[i] = 0; + if(j > 0){ + if((kk = b[0]) > b[1]) + b[1] = kk; + if((kk = b[i]) > b[i+1]) + b[i+1] = kk; + } + } + } + unlock(&profile.l); +} + +#else + +static void +memprof(int c, void *v, ulong n) +{ + USED(c); + USED(v); + USED(n); +} + +#endif + +/* main and image memory */ +static void +memprofmi(int c, ulong pc, ulong v, ulong n) +{ + USED(pc); + + if(c&2){ + if(!(mprofiler&Mimage)) + return; + } + else{ + if(!(mprofiler&Mmain)) + return; + } + switch(c){ + case 0: + c = Mmalloc; + break; + case 2: + c = Mialloc; + break; + case 0 | 1<<8: + c = Mmfree; + break; + case 2 | 1<<8: + c = Mifree; + break; + default: + print("bad profile code %d\n", c); + } + memprof(c, (void*)v, n); +} + +Dev profdevtab = { + 'P', + "prof", + + devinit, + profattach, + profwalk, + profstat, + profopen, + devcreate, + profclose, + profread, + devbread, + profwrite, + devbwrite, + devremove, + profwstat +}; diff --git a/emu/port/devprog.c b/emu/port/devprog.c new file mode 100644 index 00000000..8ee6a75b --- /dev/null +++ b/emu/port/devprog.c @@ -0,0 +1,1507 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include <interp.h> +#include <isa.h> +#include "runt.h" + +/* + * Enable the heap device for environments that allow debugging => + * Must be 1 for a production environment. + */ +int SECURE = 0; + +enum +{ + Qdir, + Qctl, + Qdbgctl, + Qheap, + Qns, + Qnsgrp, + Qpgrp, + Qstack, + Qstatus, + Qtext, + Qwait, + Qfd, + Qexception, +}; + +/* + * must be in same order as enum + */ +Dirtab progdir[] = +{ + "ctl", {Qctl}, 0, 0200, + "dbgctl", {Qdbgctl}, 0, 0600, + "heap", {Qheap}, 0, 0600, + "ns", {Qns}, 0, 0400, + "nsgrp", {Qnsgrp}, 0, 0444, + "pgrp", {Qpgrp}, 0, 0444, + "stack", {Qstack}, 0, 0400, + "status", {Qstatus}, 0, 0444, + "text", {Qtext}, 0, 0000, + "wait", {Qwait}, 0, 0400, + "fd", {Qfd}, 0, 0400, + "exception", {Qexception}, 0, 0400, +}; + +enum +{ + CMkill, + CMkillgrp, + CMrestricted, + CMexceptions, + CMprivate +}; + +static +Cmdtab progcmd[] = { + CMkill, "kill", 1, + CMkillgrp, "killgrp", 1, + CMrestricted, "restricted", 1, + CMexceptions, "exceptions", 2, + CMprivate, "private", 1, +}; + +enum +{ + CDstep, + CDtoret, + CDcont, + CDstart, + CDstop, + CDunstop, + CDmaim, + CDbpt +}; + +static +Cmdtab progdbgcmd[] = { + CDstep, "step", 0, /* known below to be first, to cope with stepN */ + CDtoret, "toret", 1, + CDcont, "cont", 1, + CDstart, "start", 1, + CDstop, "stop", 1, + CDunstop, "unstop", 1, + CDmaim, "maim", 1, + CDbpt, "bpt", 4, +}; + +typedef struct Heapqry Heapqry; +struct Heapqry +{ + char fmt; + ulong addr; + ulong module; + int count; +}; + +typedef struct Bpt Bpt; + +struct Bpt +{ + Bpt *next; + int pc; + char *file; + char path[1]; +}; + +typedef struct Progctl Progctl; +struct Progctl +{ + Rendez r; + int ref; + Proc *debugger; /* waiting for dbgxec */ + char *msg; /* reply from dbgxec */ + int step; /* instructions to try */ + int stop; /* stop running the program */ + Bpt* bpts; /* active breakpoints */ + Queue* q; /* status queue */ +}; + +#define QSHIFT 4 /* location in qid of pid */ +#define QID(q) (((ulong)(q).path&0x0000000F)>>0) +#define QPID(pid) (((pid)<<QSHIFT)) +#define PID(q) ((q).vers) +#define PATH(q) ((ulong)(q).path&~((1<<QSHIFT)-1)) + +static char *progstate[] = /* must correspond to include/interp.h */ +{ + "alt", /* blocked in alt instruction */ + "send", /* waiting to send */ + "recv", /* waiting to recv */ + "debug", /* debugged */ + "ready", /* ready to be scheduled */ + "release", /* interpreter released */ + "exiting", /* exit because of kill or error */ + "broken", /* thread crashed */ +}; + +static void dbgstep(Progctl*, Prog*, int); +static void dbgstart(Prog*); +static void freebpts(Bpt*); +static Bpt* delbpt(Bpt*, char*, int); +static Bpt* setbpt(Bpt*, char*, int); +static void mntscan(Mntwalk*, Pgrp*); +extern Type *Trdchan; +extern Type *Twrchan; +extern Module* modules; +static char Emisalign[] = "misaligned address"; + +static int +proggen(Chan *c, char *name, Dirtab *tab, int ntab, int s, Dir *dp) +{ + Qid qid; + Prog *p; + char *e; + Osenv *o; + ulong pid, path, perm, len; + + USED(ntab); + + if(s == DEVDOTDOT){ + mkqid(&qid, Qdir, 0, QTDIR); + devdir(c, qid, "#p", 0, eve, DMDIR|0555, dp); + return 1; + } + + if((ulong)c->qid.path == Qdir) { + if(name != nil){ + /* ignore s and use name to find pid */ + pid = strtoul(name, &e, 0); + if(pid == 0 || *e != '\0') + return -1; + acquire(); + p = progpid(pid); + if(p == nil){ + release(); + return -1; + } + }else{ + acquire(); + p = progn(s); + if(p == nil) { + release(); + return -1; + } + pid = p->pid; + } + o = p->osenv; + sprint(up->genbuf, "%lud", pid); + if(name != nil && strcmp(name, up->genbuf) != 0){ + release(); + return -1; + } + mkqid(&qid, pid<<QSHIFT, pid, QTDIR); + devdir(c, qid, up->genbuf, 0, o->user, DMDIR|0555, dp); + release(); + return 1; + } + + if(s >= nelem(progdir)) + return -1; + tab = &progdir[s]; + path = PATH(c->qid); + + acquire(); + p = progpid(PID(c->qid)); + if(p == nil) { + release(); + return -1; + } + + o = p->osenv; + + perm = tab->perm; + if((perm & 7) == 0) + perm = (perm|(perm>>3)|(perm>>6)) & o->pgrp->progmode; + + len = tab->length; + mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE); + devdir(c, qid, tab->name, len, o->user, perm, dp); + release(); + return 1; +} + +static Chan* +progattach(char *spec) +{ + return devattach('p', spec); +} + +static Walkqid* +progwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, proggen); +} + +static int +progstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, proggen); +} + +static Chan* +progopen(Chan *c, int omode) +{ + Prog *p; + Osenv *o; + Progctl *ctl; + int perm; + + if(c->qid.type & QTDIR) + return devopen(c, omode, 0, 0, proggen); + + acquire(); + if (waserror()) { + release(); + nexterror(); + } + p = progpid(PID(c->qid)); + if(p == nil) + error(Ethread); + o = p->osenv; + perm = progdir[QID(c->qid)-1].perm; + if((perm & 7) == 0) + perm = (perm|(perm>>3)|(perm>>6)) & o->pgrp->progmode; + devpermcheck(o->user, perm, omode); + omode = openmode(omode); + + switch(QID(c->qid)){ + default: + error(Egreg); + case Qnsgrp: + case Qpgrp: + case Qtext: + case Qstatus: + case Qstack: + case Qctl: + case Qfd: + case Qexception: + break; + case Qwait: + c->aux = qopen(1024, Qmsg, nil, nil); + if(c->aux == nil) + error(Enomem); + o->childq = c->aux; + break; + case Qns: + c->aux = malloc(sizeof(Mntwalk)); + if(c->aux == nil) + error(Enomem); + break; + case Qheap: + if(SECURE || o->pgrp->privatemem || omode != ORDWR) + error(Eperm); + c->aux = malloc(sizeof(Heapqry)); + if(c->aux == nil) + error(Enomem); + break; + case Qdbgctl: + if(SECURE || o->pgrp->privatemem || omode != ORDWR) + error(Eperm); + ctl = malloc(sizeof(Progctl)); + if(ctl == nil) + error(Enomem); + ctl->q = qopen(1024, Qmsg, nil, nil); + if(ctl->q == nil) { + free(ctl); + error(Enomem); + } + ctl->bpts = nil; + ctl->ref = 1; + c->aux = ctl; + break; + } + if(p->state != Pexiting) + c->qid.vers = p->pid; + + poperror(); + release(); + c->offset = 0; + c->mode = omode; + c->flag |= COPEN; + return c; +} + +static int +progwstat(Chan *c, uchar *db, int n) +{ + Dir d; + Prog *p; + char *u; + Osenv *o; + + if(c->qid.type&QTDIR) + error(Eperm); + acquire(); + p = progpid(PID(c->qid)); + if(p == nil) { + release(); + error(Ethread); + } + + u = up->env->user; + o = p->osenv; + if(strcmp(u, o->user) != 0 && strcmp(u, eve) != 0) { + release(); + error(Eperm); + } + + n = convM2D(db, n, &d, nil); + if(n == 0){ + release(); + error(Eshortstat); + } + if(d.mode != ~0UL) + o->pgrp->progmode = d.mode&0777; + release(); + return n; +} + +static void +closedbgctl(Progctl *ctl, Prog *p) +{ + Osenv *o; + + if(ctl->ref-- > 1) + return; + freebpts(ctl->bpts); + if(p != nil){ + o = p->osenv; + if(o->debug == ctl){ + o->debug = nil; + p->xec = xec; + } + } + qfree(ctl->q); + free(ctl); +} + +static void +progclose(Chan *c) +{ + int i; + Prog *f; + Osenv *o; + Progctl *ctl; + + switch(QID(c->qid)) { + case Qns: + case Qheap: + free(c->aux); + break; + case Qdbgctl: + if((c->flag & COPEN) == 0) + return; + ctl = c->aux; + acquire(); + closedbgctl(ctl, progpid(PID(c->qid))); + release(); + break; + case Qwait: + acquire(); + i = 0; + for(;;) { + f = progn(i++); + if(f == nil) + break; + o = f->osenv; + if(o->waitq == c->aux) + o->waitq = nil; + if(o->childq == c->aux) + o->childq = nil; + } + release(); + qfree(c->aux); + } +} + +static int +progsize(Prog *p) +{ + int size; + Frame *f; + uchar *fp; + Modlink *m; + + m = p->R.M; + size = 0; + if(m->MP != H) + size += hmsize(D2H(m->MP)); + if(m->prog != nil) + size += msize(m->prog); + + fp = p->R.FP; + while(fp != nil) { + f = (Frame*)fp; + fp = f->fp; + if(f->mr != nil) { + if(f->mr->MP != H) + size += hmsize(D2H(f->mr->MP)); + if(f->mr->prog != nil) + size += msize(f->mr->prog); + } + if(f->t == nil) + size += msize(SEXTYPE(f)); + } + return size/1024; +} + +static long +progoffset(long offset, char *va, int *np) +{ + if(offset > 0) { + offset -= *np; + if(offset < 0) { + memmove(va, va+*np+offset, -offset); + *np = -offset; + } + else + *np = 0; + } + return offset; +} + +static int +progqidwidth(Chan *c) +{ + char buf[32]; + + return sprint(buf, "%lud", c->qid.vers); +} + +int +progfdprint(Chan *c, int fd, int w, char *s, int ns) +{ + int n; + + if(w == 0) + w = progqidwidth(c); + n = snprint(s, ns, "%3d %.2s %C %4ld (%.16llux %*lud %.2ux) %5ld %8lld %s\n", + fd, + &"r w rw"[(c->mode&3)<<1], + devtab[c->type]->dc, c->dev, + c->qid.path, w, c->qid.vers, c->qid.type, + c->iounit, c->offset, c->name->s); + return n; +} + +static int +progfds(Osenv *o, char *va, int count, long offset) +{ + Fgrp *f; + Chan *c; + int n, i, w, ww; + + f = o->fgrp; /* f is not locked because we've acquired */ + n = readstr(0, va, count, o->pgrp->dot->name->s); + n += snprint(va+n, count-n, "\n"); + offset = progoffset(offset, va, &n); + /* compute width of qid.path */ + w = 0; + for(i = 0; i <= f->maxfd; i++) { + c = f->fd[i]; + if(c == nil) + continue; + ww = progqidwidth(c); + if(ww > w) + w = ww; + } + for(i = 0; i <= f->maxfd; i++) { + c = f->fd[i]; + if(c == nil) + continue; + n += progfdprint(c, i, w, va+n, count-n); + offset = progoffset(offset, va, &n); + } + return n; +} + +Inst * +pc2dispc(Inst *pc, Module *mod) +{ + ulong l, u, m, v; + ulong *tab = mod->pctab; + + v = (ulong)pc - (ulong)mod->prog; + l = 0; + u = mod->nprog-1; + while(l < u){ + m = (l+u+1)/2; + if(tab[m] < v) + l = m; + else if(tab[m] > v) + u = m-1; + else + l = u = m; + } + if(l == u && tab[u] <= v && u != mod->nprog-1 && tab[u+1] > v) + return &mod->prog[u]; + return 0; +} + +static int +progstack(REG *reg, int state, char *va, int count, long offset) +{ + int n; + Frame *f; + Inst *pc; + uchar *fp; + Modlink *m; + + n = 0; + m = reg->M; + fp = reg->FP; + pc = reg->PC; + + /* + * all states other than debug and ready block, + * but interp has already advanced the PC + */ + if(!m->compiled && state != Pready && state != Pdebug && pc > m->prog) + pc--; + if(m->compiled && m->m->pctab != nil) + pc = pc2dispc(pc, m->m); + + while(fp != nil) { + f = (Frame*)fp; + n += snprint(va+n, count-n, "%.8lux %.8lux %.8lux %.8lux %d %s\n", + (ulong)f, /* FP */ + (ulong)(pc - m->prog), /* PC in dis instructions */ + (ulong)m->MP, /* MP */ + (ulong)m->prog, /* Code for module */ + m->compiled && m->m->pctab == nil, /* True if native assembler: fool stack utility for now */ + m->m->path); /* File system path */ + + if(offset > 0) { + offset -= n; + if(offset < 0) { + memmove(va, va+n+offset, -offset); + n = -offset; + } + else + n = 0; + } + + pc = f->lr; + fp = f->fp; + if(f->mr != nil) + m = f->mr; + if(!m->compiled) + pc--; + else if(m->m->pctab != nil) + pc = pc2dispc(pc, m->m)-1; + } + return n; +} + +static int +calldepth(REG *reg) +{ + int n; + uchar *fp; + + n = 0; + for(fp = reg->FP; fp != nil; fp = ((Frame*)fp)->fp) + n++; + return n; +} + +static int +progheap(Heapqry *hq, char *va, int count, ulong offset) +{ + WORD *w; + void *p; + List *hd; + Array *a; + char *fmt, *str; + Module *m; + Modlink *ml; + Channel *c; + ulong addr; + String *ss; + union { REAL r; LONG l; WORD w[2]; } rock; + int i, s, n, len, signed_off; + Type *t; + + n = 0; + s = 0; + signed_off = offset; + addr = hq->addr; + for(i = 0; i < hq->count; i++) { + switch(hq->fmt) { + case 'W': + if(addr & 3) + return -1; + n += snprint(va+n, count-n, "%d\n", *(WORD*)addr); + s = sizeof(WORD); + break; + case 'B': + n += snprint(va+n, count-n, "%d\n", *(BYTE*)addr); + s = sizeof(BYTE); + break; + case 'V': + if(addr & 3) + return -1; + w = (WORD*)addr; + rock.w[0] = w[0]; + rock.w[1] = w[1]; + n += snprint(va+n, count-n, "%lld\n", rock.l); + s = sizeof(LONG); + break; + case 'R': + if(addr & 3) + return -1; + w = (WORD*)addr; + rock.w[0] = w[0]; + rock.w[1] = w[1]; + n += snprint(va+n, count-n, "%g\n", rock.r); + s = sizeof(REAL); + break; + case 'I': + if(addr & 3) + return -1; + for(m = modules; m != nil; m = m->link) + if(m == (Module*)hq->module) + break; + if(m == nil) + error(Ebadctl); + addr = (ulong)(m->prog+addr); + n += snprint(va+n, count-n, "%D\n", (Inst*)addr); + s = sizeof(Inst); + break; + case 'P': + if(addr & 3) + return -1; + p = *(void**)addr; + fmt = "nil\n"; + if(p != H) + fmt = "%lux\n"; + n += snprint(va+n, count-n, fmt, p); + s = sizeof(WORD); + break; + case 'L': + if(addr & 3) + return -1; + hd = *(List**)addr; + if(hd == H || D2H(hd)->t != &Tlist) + return -1; + n += snprint(va+n, count-n, "%lux.%lux\n", (ulong)&hd->tail, (ulong)hd->data); + s = sizeof(WORD); + break; + case 'A': + if(addr & 3) + return -1; + a = *(Array**)addr; + if(a == H) + n += snprint(va+n, count-n, "nil\n"); + else { + if(D2H(a)->t != &Tarray) + return -1; + n += snprint(va+n, count-n, "%d.%lux\n", a->len, (ulong)a->data); + } + s = sizeof(WORD); + break; + case 'C': + if(addr & 3) + return -1; + ss = *(String**)addr; + if(ss == H) + ss = &snil; + else + if(D2H(ss)->t != &Tstring) + return -1; + n += snprint(va+n, count-n, "%d.", abs(ss->len)); + str = string2c(ss); + len = strlen(str); + if(count-n < len) + len = count-n; + if(len > 0) { + memmove(va+n, str, len); + n += len; + } + break; + case 'M': + if(addr & 3) + return -1; + ml = *(Modlink**)addr; + fmt = ml == H ? "nil\n" : "%lux\n"; + n += snprint(va+n, count-n, fmt, ml->MP); + s = sizeof(WORD); + break; + case 'c': + if(addr & 3) + return -1; + c = *(Channel**)addr; + if(c == H) + n += snprint(va+n, count-n, "nil\n"); + else{ + t = D2H(c)->t; + if(t != &Tchannel && t != Trdchan && t != Twrchan) + return -1; + if(c->buf == H) + n += snprint(va+n, count-n, "0.%lux\n", (ulong)c); + else + n += snprint(va+n, count-n, "%d.%lux.%d.%d\n", c->buf->len, (ulong)c->buf->data, c->front, c->size); + } + break; + + } + addr += s; + if(signed_off > 0) { + signed_off -= n; + if(signed_off < 0) { + memmove(va, va+n+signed_off, -signed_off); + n = -signed_off; + } + else + n = 0; + } + } + return n; +} + +WORD +modstatus(REG *r, char *ptr, int len) +{ + Inst *PC; + Frame *f; + + if(r->M->m->name[0] == '$') { + f = (Frame*)r->FP; + snprint(ptr, len, "%s[%s]", f->mr->m->name, r->M->m->name); + if(f->mr->compiled) + return (WORD)f->lr; + return f->lr - f->mr->prog; + } + memmove(ptr, r->M->m->name, len); + if(r->M->compiled) + return (WORD)r->PC; + PC = r->PC; + /* should really check for blocked states */ + if(PC > r->M->prog) + PC--; + return PC - r->M->prog; +} + +static void +int2flag(int flag, char *s) +{ + if(flag == 0){ + *s = '\0'; + return; + } + *s++ = '-'; + if(flag & MAFTER) + *s++ = 'a'; + if(flag & MBEFORE) + *s++ = 'b'; + if(flag & MCREATE) + *s++ = 'c'; + if(flag & MCACHE) + *s++ = 'C'; + *s = '\0'; +} + +static char* +progtime(ulong msec, char *buf, char *ebuf) +{ + int tenths, sec; + + tenths = msec/100; + sec = tenths/10; + seprint(buf, ebuf, "%4d:%2.2d.%d", sec/60, sec%60, tenths%10); + return buf; +} + +static long +progread(Chan *c, void *va, long n, vlong offset) +{ + int i; + Prog *p; + Osenv *o; + Mntwalk *mw; + ulong grpid; + char *a = va; + Progctl *ctl; + char mbuf[64], timebuf[12]; + char flag[10]; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, proggen); + + switch(QID(c->qid)){ + case Qdbgctl: + ctl = c->aux; + return qread(ctl->q, va, n); + case Qstatus: + acquire(); + p = progpid(PID(c->qid)); + if(p == nil || p->state == Pexiting || p->R.M == H) { + release(); + snprint(up->genbuf, sizeof(up->genbuf), "%8lud %8d %10s %s %10s %5dK %s", + PID(c->qid), + 0, + eve, + progtime(0, timebuf, timebuf+sizeof(timebuf)), + progstate[Pexiting], + 0, + "[$Sys]"); + return readstr(offset, va, n, up->genbuf); + } + modstatus(&p->R, mbuf, sizeof(mbuf)); + o = p->osenv; + snprint(up->genbuf, sizeof(up->genbuf), "%8d %8d %10s %s %10s %5dK %s", + p->pid, + p->group!=nil? p->group->id: 0, + o->user, + progtime(p->ticks, timebuf, timebuf+sizeof(timebuf)), + progstate[p->state], + progsize(p), + mbuf); + release(); + return readstr(offset, va, n, up->genbuf); + case Qwait: + return qread(c->aux, va, n); + case Qns: + acquire(); + if(waserror()){ + release(); + nexterror(); + } + p = progpid(PID(c->qid)); + if(p == nil) + error(Ethread); + mw = c->aux; + if(mw->cddone){ + poperror(); + release(); + return 0; + } + o = p->osenv; + mntscan(mw, o->pgrp); + if(mw->mh == 0) { + mw->cddone = 1; + i = snprint(a, n, "cd %s\n", o->pgrp->dot->name->s); + poperror(); + release(); + return i; + } + int2flag(mw->cm->mflag, flag); + if(strcmp(mw->cm->to->name->s, "#M") == 0){ + i = snprint(a, n, "mount %s %s %s %s\n", flag, + mw->cm->to->mchan->name->s, + mw->mh->from->name->s, mw->cm->spec? mw->cm->spec : ""); + }else + i = snprint(a, n, "bind %s %s %s\n", flag, + mw->cm->to->name->s, mw->mh->from->name->s); + poperror(); + release(); + return i; + case Qnsgrp: + acquire(); + p = progpid(PID(c->qid)); + if(p == nil) { + release(); + error(Ethread); + } + grpid = ((Osenv *)p->osenv)->pgrp->pgrpid; + release(); + return readnum(offset, va, n, grpid, NUMSIZE); + case Qpgrp: + acquire(); + p = progpid(PID(c->qid)); + if(p == nil) { + release(); + error(Ethread); + } + grpid = p->group!=nil? p->group->id: 0; + release(); + return readnum(offset, va, n, grpid, NUMSIZE); + case Qstack: + acquire(); + p = progpid(PID(c->qid)); + if(p == nil || p->state == Pexiting) { + release(); + error(Ethread); + } + if(p->state == Pready) { + release(); + error(Estopped); + } + n = progstack(&p->R, p->state, va, n, offset); + release(); + return n; + case Qheap: + acquire(); + if(waserror()){ + release(); + nexterror(); + } + n = progheap(c->aux, va, n, offset); + if(n == -1) + error(Emisalign); + poperror(); + release(); + return n; + case Qfd: + acquire(); + if(waserror()) { + release(); + nexterror(); + } + p = progpid(PID(c->qid)); + if(p == nil) + error(Ethread); + o = p->osenv; + n = progfds(o, va, n, offset); + poperror(); + release(); + return n; + case Qexception: + acquire(); + p = progpid(PID(c->qid)); + if(p == nil) { + release(); + error(Ethread); + } + if(p->exstr == nil) + up->genbuf[0] = 0; + else + snprint(up->genbuf, sizeof(up->genbuf), p->exstr); + release(); + return readstr(offset, va, n, up->genbuf); + } + error(Egreg); + return 0; +} + +static void +mntscan(Mntwalk *mw, Pgrp *pg) +{ + Mount *t; + Mhead *f; + int nxt, i; + ulong last, bestmid; + + rlock(&pg->ns); + + nxt = 0; + bestmid = ~0; + + last = 0; + if(mw->mh) + last = mw->cm->mountid; + + for(i = 0; i < MNTHASH; i++) { + for(f = pg->mnthash[i]; f; f = f->hash) { + for(t = f->mount; t; t = t->next) { + if(mw->mh == 0 || + (t->mountid > last && t->mountid < bestmid)) { + mw->cm = t; + mw->mh = f; + bestmid = mw->cm->mountid; + nxt = 1; + } + } + } + } + if(nxt == 0) + mw->mh = 0; + + runlock(&pg->ns); +} + +static long +progwrite(Chan *c, void *va, long n, vlong offset) +{ + Prog *p, *f; + Heapqry *hq; + char buf[512]; + Progctl *ctl; + char *b; + int i, pc; + Cmdbuf *cb; + Cmdtab *ct; + Osenv *o; + + USED(offset); + USED(va); + + if(c->qid.type & QTDIR) + error(Eisdir); + + acquire(); + if(waserror()) { + release(); + nexterror(); + } + p = progpid(PID(c->qid)); + if(p == nil) + error(Ethread); + + switch(QID(c->qid)){ + case Qctl: + cb = parsecmd(va, n); + if(waserror()){ + free(cb); + nexterror(); + } + ct = lookupcmd(cb, progcmd, nelem(progcmd)); + switch(ct->index){ + case CMkillgrp: + killgrp(p, "killed"); + break; + case CMkill: + killprog(p, "killed"); + break; + case CMrestricted: + p->flags |= Prestrict; + break; + case CMexceptions: + if(p->group->id != p->pid) + error(Eperm); + if(strcmp(cb->f[1], "propagate") == 0) + p->flags |= Ppropagate; + else if(strcmp(cb->f[1], "notifyleader") == 0) + p->flags |= Pnotifyleader; + else + error(Ebadctl); + break; + case CMprivate: + o = p->osenv; + o->pgrp->privatemem = 1; + break; + } + poperror(); + free(cb); + break; + case Qdbgctl: + cb = parsecmd(va, n); + if(waserror()){ + free(cb); + nexterror(); + } + if(cb->nf == 1 && strncmp(cb->f[0], "step", 4) == 0) + ct = progdbgcmd; + else + ct = lookupcmd(cb, progdbgcmd, nelem(progdbgcmd)); + switch(ct->index){ + case CDstep: + if(cb->nf == 1) + i = strtoul(cb->f[0]+4, nil, 0); + else + i = strtoul(cb->f[1], nil, 0); + dbgstep(c->aux, p, i); + break; + case CDtoret: + f = currun(); + i = calldepth(&p->R); + while(f->kill == nil) { + dbgstep(c->aux, p, 1024); + if(i > calldepth(&p->R)) + break; + } + break; + case CDcont: + f = currun(); + while(f->kill == nil) + dbgstep(c->aux, p, 1024); + break; + case CDstart: + dbgstart(p); + break; + case CDstop: + ctl = c->aux; + ctl->stop = 1; + break; + case CDunstop: + ctl = c->aux; + ctl->stop = 0; + break; + case CDbpt: + pc = strtoul(cb->f[3], nil, 10); + ctl = c->aux; + if(strcmp(cb->f[1], "set") == 0) + ctl->bpts = setbpt(ctl->bpts, cb->f[2], pc); + else if(strcmp(cb->f[1], "del") == 0) + ctl->bpts = delbpt(ctl->bpts, cb->f[2], pc); + else + error(Ebadctl); + break; + case CDmaim: + p->kill = "maim"; + break; + } + poperror(); + free(cb); + break; + case Qheap: + /* + * Heap query: + * addr.Fn + * pc+module.In + */ + i = n; + if(i > sizeof(buf)-1) + i = sizeof(buf)-1; + memmove(buf, va, i); + buf[i] = '\0'; + hq = c->aux; + hq->addr = strtoul(buf, &b, 0); + if(*b == '+') + hq->module = strtoul(b, &b, 0); + if(*b++ != '.') + error(Ebadctl); + hq->fmt = *b++; + hq->count = strtoul(b, nil, 0); + break; + default: + print("unknown qid in procwrite\n"); + error(Egreg); + } + poperror(); + release(); + return n; +} + +static Bpt* +setbpt(Bpt *bpts, char *path, int pc) +{ + int n; + Bpt *b; + + n = strlen(path); + b = mallocz(sizeof *b + n, 0); + if(b == nil) + return bpts; + b->pc = pc; + memmove(b->path, path, n+1); + b->file = b->path; + path = strrchr(b->path, '/'); + if(path != nil) + b->file = path + 1; + b->next = bpts; + return b; +} + +static Bpt* +delbpt(Bpt *bpts, char *path, int pc) +{ + Bpt *b, **last; + + last = &bpts; + for(b = bpts; b != nil; b = b->next){ + if(b->pc == pc && strcmp(b->path, path) == 0) { + *last = b->next; + free(b); + break; + } + last = &b->next; + } + return bpts; +} + +static void +freebpts(Bpt *b) +{ + Bpt *next; + + for(; b != nil; b = next){ + next = b->next; + free(b); + } +} + +static void +telldbg(Progctl *ctl, char *msg) +{ + kstrcpy(ctl->msg, msg, ERRMAX); + ctl->debugger = nil; + Wakeup(&ctl->r); +} + +static void +dbgstart(Prog *p) +{ + Osenv *o; + Progctl *ctl; + + o = p->osenv; + ctl = o->debug; + if(ctl != nil && ctl->debugger != nil) + error("prog debugged"); + if(p->state == Pdebug) + addrun(p); + o->debug = nil; + p->xec = xec; +} + +static int +xecdone(void *vc) +{ + Progctl *ctl = vc; + + return ctl->debugger == nil; +} + +static void +dbgstep(Progctl *vctl, Prog *p, int n) +{ + Osenv * volatile o; + Progctl * volatile ctl; + char buf[ERRMAX+20], *msg; + + if(p == currun()) + error("cannot step yourself"); + + if(p->state == Pbroken) + error("prog broken"); + + ctl = vctl; + if(ctl->debugger != nil) + error("prog already debugged"); + + o = p->osenv; + if(o->debug == nil) { + o->debug = ctl; + p->xec = dbgxec; + }else if(o->debug != ctl) + error("prog already debugged"); + + ctl->step = n; + if(p->state == Pdebug) + addrun(p); + ctl->debugger = up; + strcpy(buf, "child: "); + msg = buf+7; + ctl->msg = msg; + + /* + * wait for reply from dbgxec; release is okay because prog is now + * debugged, cannot exit. + */ + if(waserror()){ + acquire(); + ctl->debugger = nil; + ctl->msg = nil; + o->debug = nil; + p->xec = xec; + nexterror(); + } + release(); + Sleep(&ctl->r, xecdone, ctl); + poperror(); + acquire(); + if(msg[0] != '\0') + error(buf); +} + +void +dbgexit(Prog *kid, int broken, char *estr) +{ + int n; + Osenv *e; + Progctl *ctl; + char buf[ERRMAX+20]; + + e = kid->osenv; + ctl = e->debug; + e->debug = nil; + kid->xec = xec; + + if(broken) + n = snprint(buf, sizeof(buf), "broken: %s", estr); + else + n = snprint(buf, sizeof(buf), "exited"); + if(ctl->debugger) + telldbg(ctl, buf); + qproduce(ctl->q, buf, n); +} + +static void +dbgaddrun(Prog *p) +{ + Osenv *o; + + p->state = Pdebug; + p->addrun = nil; + o = p->osenv; + if(o->rend != nil) + Wakeup(o->rend); + o->rend = nil; +} + +static int +bdone(void *vp) +{ + Prog *p = vp; + + return p->addrun == nil; +} + +static void +dbgblock(Prog *p) +{ + Osenv *o; + Progctl *ctl; + + o = p->osenv; + ctl = o->debug; + qproduce(ctl->q, progstate[p->state], strlen(progstate[p->state])); + pushrun(p); + p->addrun = dbgaddrun; + o->rend = &up->sleep; + + /* + * bdone(p) is safe after release because p is being debugged, + * so cannot exit. + */ + if(waserror()){ + acquire(); + nexterror(); + } + release(); + if(o->rend != nil) + Sleep(o->rend, bdone, p); + poperror(); + acquire(); + if(p->kill != nil) + error(p->kill); + ctl = o->debug; + if(ctl != nil) + qproduce(ctl->q, "run", 3); +} + +void +dbgxec(Prog *p) +{ + Bpt *b; + Prog *kid; + Osenv *env; + Progctl *ctl; + int op, pc, n; + char buf[ERRMAX+10]; + extern void (*dec[])(void); + + env = p->osenv; + ctl = env->debug; + ctl->ref++; + if(waserror()){ + closedbgctl(ctl, p); + nexterror(); + } + + R = p->R; + R.MP = R.M->MP; + + R.IC = p->quanta; + if((ulong)R.IC > ctl->step) + R.IC = ctl->step; + if(ctl->stop) + R.IC = 0; + + + buf[0] = '\0'; + + if(R.IC != 0 && R.M->compiled) { + comvec(); + if(p != currun()) + dbgblock(p); + goto save; + } + + while(R.IC != 0) { + if(0) + print("step: %lux: %s %4ld %D\n", + (ulong)p, R.M->m->name, R.PC-R.M->prog, R.PC); + + dec[R.PC->add](); + op = R.PC->op; + R.PC++; + optab[op](); + + /* + * check notification about new progs + */ + if(op == ISPAWN || op == IMSPAWN) { + /* pick up the kid from the end of the run queue */ + kid = delruntail(Pdebug); + n = snprint(buf, sizeof buf, "new %d", kid->pid); + qproduce(ctl->q, buf, n); + buf[0] = '\0'; + } + if(op == ILOAD) { + n = snprint(buf, sizeof buf, "load %s", string2c(*(String**)R.s)); + qproduce(ctl->q, buf, n); + buf[0] = '\0'; + } + + /* + * check for returns at big steps + */ + if(op == IRET) + R.IC = 1; + + /* + * check for blocked progs + */ + if(R.IC == 1 && p != currun()) + dbgblock(p); + if(ctl->stop) + R.IC = 1; + R.IC--; + + if(ctl->bpts == nil) + continue; + + pc = R.PC - R.M->prog; + for(b = ctl->bpts; b != nil; b = b->next) { + if(pc == b->pc && + (strcmp(R.M->m->path, b->path) == 0 || + strcmp(R.M->m->path, b->file) == 0)) { + strcpy(buf, "breakpoint"); + goto save; + } + } + } +save: + if(ctl->stop) + strcpy(buf, "stopped"); + + p->R = R; + + if(env->debug == nil) { + poperror(); + return; + } + + if(p == currun()) + delrun(Pdebug); + + telldbg(env->debug, buf); + poperror(); + closedbgctl(env->debug, p); +} + +Dev progdevtab = { + 'p', + "prog", + + devinit, + progattach, + progwalk, + progstat, + progopen, + devcreate, + progclose, + progread, + devbread, + progwrite, + devbwrite, + devremove, + progwstat +}; diff --git a/emu/port/devroot.c b/emu/port/devroot.c new file mode 100644 index 00000000..695d151c --- /dev/null +++ b/emu/port/devroot.c @@ -0,0 +1,150 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +extern Rootdata rootdata[]; +extern Dirtab roottab[]; +extern int rootmaxq; + +static Chan* +rootattach(char *spec) +{ + int i; + ulong len; + Rootdata *r; + + if(*spec) + error(Ebadspec); + for (i = 0; i < rootmaxq; i++){ + r = &rootdata[i]; + if (r->sizep){ + len = *r->sizep; + r->size = len; + roottab[i].length = len; + } + } + return devattach('/', spec); +} + +static int +rootgen(Chan *c, char *name, Dirtab *tab, int nd, int s, Dir *dp) +{ + int p, i; + Rootdata *r; + + if(s == DEVDOTDOT){ + p = rootdata[c->qid.path].dotdot; + c->qid.path = p; + c->qid.type = QTDIR; + name = "#/"; + if(p != 0){ + for(i = 0; i < rootmaxq; i++) + if(roottab[i].qid.path == c->qid.path){ + name = roottab[i].name; + break; + } + } + devdir(c, c->qid, name, 0, eve, 0555, dp); + return 1; + } + if(name != nil){ + isdir(c); + r = &rootdata[(int)c->qid.path]; + tab = r->ptr; + for(i=0; i<r->size; i++, tab++) + if(strcmp(tab->name, name) == 0){ + devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp); + return 1; + } + return -1; + } + if(s >= nd) + return -1; + tab += s; + devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp); + return 1; +} + +static Walkqid* +rootwalk(Chan *c, Chan *nc, char **name, int nname) +{ + ulong p; + + p = c->qid.path; + if(nname == 0) + p = rootdata[p].dotdot; + return devwalk(c, nc, name, nname, rootdata[p].ptr, rootdata[p].size, rootgen); +} + +static int +rootstat(Chan *c, uchar *dp, int n) +{ + int p; + + p = rootdata[c->qid.path].dotdot; + return devstat(c, dp, n, rootdata[p].ptr, rootdata[p].size, rootgen); +} + +static Chan* +rootopen(Chan *c, int omode) +{ + int p; + + p = rootdata[c->qid.path].dotdot; + return devopen(c, omode, rootdata[p].ptr, rootdata[p].size, rootgen); +} + +/* + * sysremove() knows this is a nop + */ +static void +rootclose(Chan *c) +{ + USED(c); +} + +static long +rootread(Chan *c, void *buf, long n, vlong offset) +{ + ulong p, len; + uchar *data; + + p = c->qid.path; + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, rootdata[p].ptr, rootdata[p].size, rootgen); + len = rootdata[p].size; + if(offset < 0 || offset >= len) + return 0; + if(offset+n > len) + n = len - offset; + data = rootdata[p].ptr; + memmove(buf, data+offset, n); + return n; +} + +static long +rootwrite(Chan *c, void *a, long n, vlong off) +{ + USED(c); USED(a); USED(n); USED(off); + error(Eperm); + return 0; +} + +Dev rootdevtab = { + '/', + "root", + + devinit, + rootattach, + rootwalk, + rootstat, + rootopen, + devcreate, + rootclose, + rootread, + devbread, + rootwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/emu/port/devsign.c b/emu/port/devsign.c new file mode 100644 index 00000000..f42d1dbb --- /dev/null +++ b/emu/port/devsign.c @@ -0,0 +1,440 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "interp.h" +#include <isa.h> +#include "runt.h" +#include "mp.h" +#include "libsec.h" +#include "../../libkeyring/keys.h" + +/* + * experimental version of signed modules + */ + +enum +{ + Qdir, + Qkey, + Qctl, + + Maxkey = 2048 +}; + +static Dirtab signdir[] = +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "signerkey", {Qkey}, 0, 0644, + "signerctl", {Qctl}, 0, 0600, +}; + +typedef struct Get Get; +struct Get { + uchar* p; + uchar* ep; +}; + +#define G32(b) ((b[0]<<24)|(b[1]<<16)|(b[2]<<8)|b[3]) + +static int vc(Get*); +static int vs(void*, int, Get*, int); +static Signerkey* findsignerkey(Skeyset*, char*, int, char*); +extern vlong osusectime(void); + +int +verifysigner(uchar *sign, int len, uchar *data, ulong ndata) +{ + Get sig; + int alg; + ulong issued, expires, now; + int footprint, r, n; + uchar buf[128], digest[SHA1dlen]; + DigestState *ds; + volatile struct {BigInt b;} b; + volatile struct {BigInt s;} s; + SigAlgVec *sa; + Signerkey *key; + Skeyset *sigs; + + /* alg[1] issued[4] expires[4] footprint[2] signer[n] sig[m] */ + sigs = up->env->sigs; + if(sigs == nil) + return 1; /* not enforcing signed modules */ + sig.p = sign; + sig.ep = sign+len; + alg = vc(&sig); + if(alg != 2) + return 0; /* we do only SHA1/RSA */ + sa = findsigalg("rsa"); + if(sa == nil) + return 0; + if(vs(buf, sizeof(buf), &sig, 4) < 0) + return 0; + now = osusectime()/1000000; + issued = G32(buf); + if(vs(buf, sizeof(buf), &sig, 4) < 0) + return 0; + if(issued != 0 && now < issued) + return 0; + expires = G32(buf); + if(expires != 0 && now >= expires) + return 0; + footprint = vc(&sig) << 8; + footprint |= vc(&sig); + if(footprint < 0) + return 0; + r = 0; + b.b = nil; + s.s = nil; + qlock(&sigs->l); + if(waserror()) + goto out; + if((n = vs(buf, sizeof(buf)-NUMSIZE-1, &sig, -1)) < 0) /* owner */ + goto out; + buf[n] = 0; + key = findsignerkey(sigs, sa->name, footprint, (char*)buf); + if(key == nil) + goto out; + n += snprint((char*)buf+n, NUMSIZE, " %lud", expires); + ds = sha1(buf, n, nil, nil); + sha1(data, ndata, digest, ds); + b.b = betomp(digest, SHA1dlen, nil); + if(b.b == nil) + goto out; + s.s = betomp(sig.p, sig.ep-sig.p, nil); + if(s.s == nil) + goto out; + r = (*sa->verify)(b.b, s.s, key->pk); +out: + qunlock(&sigs->l); + if(b.b != nil) + mpfree(b.b); + if(s.s != nil) + mpfree(s.s); + return r; +} + +int +mustbesigned(char *path, uchar *code, ulong length, Dir *dir) +{ + USED(code); USED(length); +if(0)print("load %s: %d %C\n", path, up->env->sigs!=nil, dir==nil?'?':dir->type); + /* allow only signed modules and those in #/; already loaded modules are reloaded from cache */ + return up->env->sigs != nil && (dir == nil || dir->type != '/'); +} + +static int +vc(Get *g) +{ + return g->p < g->ep? *g->p++: -1; +} + +static int +vs(void *s, int lim, Get *g, int n) +{ + int nr; + + if(n < 0){ + if(g->p >= g->ep) + return -1; + n = *g->p++; + lim--; + } + if(n > lim) + return -1; + nr = g->ep - g->p; + if(n > nr) + return -1; + if(s != nil) + memmove(s, g->p, n); + g->p += n; + return n; +} + +static char* +cstring(char *str, char **strp) +{ + char *p, *s; + int n; + + p = strchr(str, '\n'); + if(p == 0) + p = str + strlen(str); + n = p - str; + s = malloc(n+1); + if(s == nil) + return nil; + memmove(s, str, n); + s[n] = 0; + + if(strp){ + if(*p) + p++; + *strp = p; + } + + return s; +} + +static SigAlgVec* +cstrtoalg(char *str, char **strp) +{ + int n; + char *p, name[KNAMELEN]; + + p = strchr(str, '\n'); + if(p == 0){ + p = str + strlen(str); + if(strp) + *strp = p; + } else { + if(strp) + *strp = p+1; + } + + n = p - str; + if(n >= sizeof(name)) + return nil; + strncpy(name, str, n); + name[n] = 0; + return findsigalg(name); +} + +static Signerkey* +strtopk(char *buf) +{ + SigAlgVec *sa; + char *p; + Signerkey *key; + + key = malloc(sizeof(*key)); + if(key == nil) + return nil; + key->r.ref = 1; + sa = cstrtoalg(buf, &p); + if(sa == nil){ + free(key); + return nil; + } + key->alg = sa; + key->pkfree = sa->pkfree; + key->owner = cstring(p, &p); + if(key->owner == nil){ + free(key); + return nil; + } + key->pk = (*sa->str2pk)(p, &p); + if(key->pk == nil){ + free(key->owner); + free(key); + return nil; + } + return key; +} + +static Signerkey* +findsignerkey(Skeyset *sigs, char *alg, int footprint, char *owner) +{ + int i; + Signerkey *key; + + for(i=0; i<sigs->nkey; i++){ + key = sigs->keys[i]; + if(key->footprint == footprint && + strcmp(alg, ((SigAlgVec*)key->alg)->name) == 0 && + strcmp(key->owner, owner) == 0) + return key; + } + return nil; +} + +static Chan* +signattach(char *spec) +{ + return devattach(0x03A3, spec); /* L'Σ' */ +} + +static Walkqid* +signwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, signdir, nelem(signdir), devgen); +} + +static int +signstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, signdir, nelem(signdir), devgen); +} + +static Chan* +signopen(Chan *c, int omode) +{ + if(c->qid.type & QTDIR) { + if(omode != OREAD) + error(Eisdir); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; + } + + switch((ulong)c->qid.path){ + case Qctl: + if(!iseve()) + error(Eperm); + break; + + case Qkey: + if(omode != OREAD && !iseve()) + error(Eperm); + break; + } + + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +signclose(Chan *c) +{ + USED(c); +} + +static long +signread(Chan *c, void *va, long n, vlong offset) +{ + char *buf, *p; + SigAlgVec *sa; + Skeyset *sigs; + Signerkey *key; + int i; + + if(c->qid.type & QTDIR) + return devdirread(c, va, n, signdir, nelem(signdir), devgen); + sigs = up->env->sigs; + if(sigs == nil) + return 0; + switch((ulong)c->qid.path){ + case Qkey: + buf = smalloc(Maxkey); + if(waserror()){ + free(buf); + nexterror(); + } + qlock(&sigs->l); + if(waserror()){ + qunlock(&sigs->l); + nexterror(); + } + p = buf; + for(i=0; i<sigs->nkey; i++){ + key = sigs->keys[i]; + sa = key->alg; + p = seprint(p, buf+Maxkey, "owner=%s alg=%s footprint=%ud expires=%lud\n", + key->owner, sa->name, key->footprint, key->expires); + } + poperror(); + qunlock(&sigs->l); + n = readstr(offset, va, n, buf); + poperror(); + free(buf); + return n; + + case Qctl: + return readnum(offset, va, n, sigs->nkey, NUMSIZE); + } + return 0; +} + +static long +signwrite(Chan *c, void *va, long n, vlong offset) +{ + char *buf; + Skeyset *sigs; + Signerkey *okey, *key; + int i; + + if(c->qid.type & QTDIR) + error(Eisdir); + USED(offset); + switch((ulong)c->qid.path){ + case Qkey: + if(n >= Maxkey) + error(Etoobig); + buf = smalloc(Maxkey); + if(waserror()){ + free(buf); + nexterror(); + } + memmove(buf, va, n); + buf[n] = 0; + + key = strtopk(buf); + if(key == nil) + error("bad key syntax"); + poperror(); + free(buf); + + if(waserror()){ + freeskey(key); + nexterror(); + } + sigs = up->env->sigs; + if(sigs == nil){ + sigs = malloc(sizeof(*sigs)); + if(sigs == nil) + error(Enomem); + sigs->r.ref = 1; + up->env->sigs = sigs; + } + qlock(&sigs->l); + if(waserror()){ + qunlock(&sigs->l); + nexterror(); + } + for(i=0; i<sigs->nkey; i++){ + okey = sigs->keys[i]; + if(strcmp(okey->owner, key->owner) == 0){ + /* replace existing key */ + sigs->keys[i] = key; + freeskey(okey); + break; + } + } + if(i >= sigs->nkey){ + if(sigs->nkey >= nelem(sigs->keys)) + error("too many keys"); + sigs->keys[sigs->nkey++] = key; + } + poperror(); + qunlock(&sigs->l); + poperror(); /* key */ + + return n; + case Qctl: + error(Ebadctl); + break; + } + return 0; +} + +Dev signdevtab = { + 0x03A3, /* L'Σ', /* U+03A3 */ + "sign", + + devinit, + signattach, + signwalk, + signstat, + signopen, + devcreate, + signclose, + signread, + devbread, + signwrite, + devbwrite, + devremove, + devwstat +}; diff --git a/emu/port/devsnarf.c b/emu/port/devsnarf.c new file mode 100644 index 00000000..4778c130 --- /dev/null +++ b/emu/port/devsnarf.c @@ -0,0 +1,161 @@ +/* + * host's snarf buffer + */ + +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +enum{ + Qdir, + Qsnarf, + + Maxsnarf= 100*1024 +}; + +static +Dirtab snarftab[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "snarf", {Qsnarf}, 0, 0666, +}; + +static QLock snarflock; /* easiest to synchronise all access */ + +static Chan* +snarfattach(char *spec) +{ + return devattach('^', spec); +} + +static Walkqid* +snarfwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, snarftab, nelem(snarftab), devgen); +} + +static int +snarfstat(Chan* c, uchar *db, int n) +{ + return devstat(c, db, n, snarftab, nelem(snarftab), devgen); +} + +static Chan* +snarfopen(Chan* c, int omode) +{ + c = devopen(c, omode, snarftab, nelem(snarftab), devgen); + if(c->qid.path == Qsnarf){ + if(c->mode == ORDWR || c->mode == OWRITE){ + qlock(&snarflock); + free(c->aux); + c->aux = nil; + qunlock(&snarflock); + } + } + return c; +} + +static void +snarfclose(Chan* c) +{ + if((c->flag & COPEN) == 0) + return; + if(c->qid.path == Qsnarf){ + /* this must be the last reference: no need to lock */ + if(c->mode == ORDWR || c->mode == OWRITE){ + if(!waserror()){ + clipwrite(c->aux); + poperror(); + } + } + free(c->aux); + } +} + +static long +snarfread(Chan* c, void* a, long n, vlong offset) +{ + void *p; + + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, snarftab, nelem(snarftab), devgen); + case Qsnarf: + qlock(&snarflock); + if(waserror()){ + qunlock(&snarflock); + nexterror(); + } + if(offset == 0){ + p = c->aux; + c->aux = nil; + free(p); + c->aux = clipread(); + } + if(c->aux != nil) + n = readstr(offset, a, n, c->aux); + else + n = 0; + poperror(); + qunlock(&snarflock); + break; + default: + n=0; + break; + } + return n; +} + +static long +snarfwrite(Chan* c, void* va, long n, vlong offset) +{ + ulong l; + char *p; + + switch((ulong)c->qid.path){ + case Qsnarf: + /* append only */ + USED(offset); /* not */ + qlock(&snarflock); + if(waserror()){ + qunlock(&snarflock); + nexterror(); + } + if(c->aux != nil) + l = strlen(c->aux); + else + l = 0; + if(l+n > Maxsnarf) + error(Etoobig); + c->aux = realloc(c->aux, l+n+1); + if((p = c->aux) == nil) + error(Enovmem); + memmove(p+l, va, n); + p[l+n] = 0; + snarftab[1].qid.vers++; + poperror(); + qunlock(&snarflock); + break; + default: + error(Ebadusefd); + } + return n; +} + +Dev snarfdevtab = { + '^', + "snarf", + + devinit, + snarfattach, + snarfwalk, + snarfstat, + snarfopen, + devcreate, + snarfclose, + snarfread, + devbread, + snarfwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/emu/port/devsrv.c b/emu/port/devsrv.c new file mode 100644 index 00000000..3fec01fc --- /dev/null +++ b/emu/port/devsrv.c @@ -0,0 +1,725 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "interp.h" +#include <isa.h> +#include "runt.h" + +typedef struct SrvFile SrvFile; +struct SrvFile +{ + char* spec; + char* name; + char* user; + ulong perm; + vlong length; + Qid qid; + int ref; + int opens; + int flags; + Channel* read; + Channel* write; + SrvFile* entry; + SrvFile* dir; + SrvFile* devlist; +}; + +enum +{ + SORCLOSE = (1<<0), + SRDCLOSE = (1<<1), + SWRCLOSE = (1<<2), + SREMOVED = (1<<3), +}; + +typedef struct SrvDev SrvDev; +struct SrvDev +{ + Type* Rread; + Type* Rwrite; + QLock l; + ulong pathgen; + SrvFile* devices; +}; + +static SrvDev dev; + +void freechan(Heap*, int); +static void freerdchan(Heap*, int); +static void freewrchan(Heap*, int); + +Type *Trdchan; +Type *Twrchan; + +static int +srvgen(Chan *c, char *name, Dirtab *tab, int ntab, int s, Dir *dp) +{ + SrvFile *f; + + USED(name); + USED(tab); + USED(ntab); + + if(s == DEVDOTDOT){ + devdir(c, c->qid, "#s", 0, eve, 0555, dp); + return 1; + } + f = c->aux; + if((c->qid.type & QTDIR) == 0){ + if(s > 0) + return -1; + devdir(c, f->qid, f->name, f->length, f->user, f->perm, dp); + return 1; + } + + for(f = f->entry; f != nil; f = f->entry){ + if(s-- == 0) + break; + } + if(f == nil) + return -1; + + devdir(c, f->qid, f->name, f->length, f->user, f->perm, dp); + return 1; +} + +static void +srvinit(void) +{ + static uchar rmap[] = Sys_Rread_map; + static uchar wmap[] = Sys_Rwrite_map; + + Trdchan = dtype(freerdchan, sizeof(Channel), Tchannel.map, Tchannel.np); + Twrchan = dtype(freewrchan, sizeof(Channel), Tchannel.map, Tchannel.np); + + dev.pathgen = 1; + dev.Rread = dtype(freeheap, Sys_Rread_size, rmap, sizeof(rmap)); + dev.Rwrite = dtype(freeheap, Sys_Rwrite_size, wmap, sizeof(wmap)); +} + +static int +srvchkattach(SrvFile *d) +{ + if(strcmp(d->user, up->env->user) == 0) + return 1; + + /* + * Need write permission in other to allow attaches if + * we are not the owner + */ + if(d->perm & 2) + return 1; + + return 0; +} + +static Chan* +srvattach(char *spec) +{ + Chan *c; + SrvFile *d; + + if(spec[0] != '\0'){ + qlock(&dev.l); + for(d = dev.devices; d != nil; d = d->devlist){ + if(strcmp(spec, d->spec) == 0){ + if(srvchkattach(d) == 0){ + qunlock(&dev.l); + error(Eperm); + } + d->ref++; + break; + } + } + qunlock(&dev.l); + + if(d != nil){ + c = devattach('s', spec); + c->aux = d; + c->qid = d->qid; + return c; + } + } + + d = malloc(sizeof(SrvFile)); + if(d == nil) + error(Enomem); + + c = devattach('s', spec); + + d->ref = 1; + kstrdup(&d->spec, spec); + kstrdup(&d->user, up->env->user); + snprint(up->genbuf, sizeof(up->genbuf), "srv%ld", up->env->pgrp->pgrpid); + kstrdup(&d->name, up->genbuf); + d->perm = DMDIR|0770; + + qlock(&dev.l); + mkqid(&d->qid, dev.pathgen++, 0, QTDIR); + d->devlist = dev.devices; + dev.devices = d; + qunlock(&dev.l); + + c->aux = d; + c->qid = d->qid; + + return c; +} + +static Walkqid* +srvwalk(Chan *c, Chan *nc, char **name, int nname) +{ + SrvFile *d, *pd; + Walkqid *w; + + pd = c->aux; + qlock(&dev.l); + if(waserror()){ + qunlock(&dev.l); + nexterror(); + } + + w = devwalk(c, nc, name, nname, nil, 0, srvgen); + if(w != nil && w->clone != nil){ + if(nname != 0){ + for(d = pd->entry; d != nil; d = d->entry) + if(d->qid.path == w->clone->qid.path) + break; + if(d == nil) + panic("srvwalk"); + if(w->clone == c) + pd->ref--; + }else + d = pd; + w->clone->aux = d; + d->ref++; + } + poperror(); + qunlock(&dev.l); + return w; +} + +static int +srvstat(Chan *c, uchar *db, int n) +{ + qlock(&dev.l); + if(waserror()){ + qunlock(&dev.l); + nexterror(); + } + n = devstat(c, db, n, 0, 0, srvgen); + poperror(); + qunlock(&dev.l); + return n; +} + +static Chan* +srvopen(Chan *c, int omode) +{ + SrvFile *sf; + + openmode(omode); /* check it */ + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Eisdir); + c->mode = omode; + c->flag |= COPEN; + c->offset = 0; + return c; + } + + sf = c->aux; + + qlock(&dev.l); + if(waserror()){ + qunlock(&dev.l); + nexterror(); + } + devpermcheck(sf->user, sf->perm, omode); + if(omode&ORCLOSE && strcmp(sf->user, up->env->user) != 0) + error(Eperm); + if(sf->perm & DMEXCL && sf->opens != 0) + error(Einuse); + sf->opens++; + if(omode&ORCLOSE) + sf->flags |= SORCLOSE; + poperror(); + qunlock(&dev.l); + + c->offset = 0; + c->flag |= COPEN; + c->mode = openmode(omode); + + return c; +} + +static int +srvwstat(Chan *c, uchar *dp, int n) +{ + Dir *d; + SrvFile *sf, *f; + + sf = c->aux; + if(strcmp(up->env->user, sf->user) != 0) + error(Eperm); + + d = smalloc(sizeof(*d)+n); + if(waserror()){ + free(d); + nexterror(); + } + n = convM2D(dp, n, d, (char*)&d[1]); + if(n == 0) + error(Eshortstat); + if(!emptystr(d->name)){ + if(sf->dir == nil) + error(Eperm); + validwstatname(d->name); + qlock(&dev.l); + for(f = sf->dir; f != nil; f = f->entry) + if(strcmp(f->name, d->name) == 0){ + qunlock(&dev.l); + error(Eexist); + } + kstrdup(&sf->name, d->name); + qunlock(&dev.l); + } + if(d->mode != ~0UL) + sf->perm = d->mode & (DMEXCL|DMAPPEND|0777); + if(d->length != (vlong)-1) + sf->length = d->length; + poperror(); + free(d); + return n; +} + +static void +srvputdir(SrvFile *sf) +{ + SrvFile **l, *d; + + sf->ref--; + if(sf->ref != 0) + return; + + for(l = &dev.devices; (d = *l) != nil; l = &d->devlist) + if(d == sf){ + *l = d->devlist; + break; + } + free(sf->spec); + free(sf->user); + free(sf->name); + free(sf); +} + +static void +srvunblock(SrvFile *sf, int fid) +{ + Channel *d; + Sys_FileIO_read rreq; + Sys_FileIO_write wreq; + + acquire(); + if(waserror()){ + release(); + nexterror(); + } + d = sf->read; + if(d != H){ + rreq.t0 = 0; + rreq.t1 = 0; + rreq.t2 = fid; + rreq.t3 = H; + csendalt(d, &rreq, d->mid.t, -1); + } + + d = sf->write; + if(d != H){ + wreq.t0 = 0; + wreq.t1 = H; + wreq.t2 = fid; + wreq.t3 = H; + csendalt(d, &wreq, d->mid.t, -1); + } + poperror(); + release(); +} + +static void +srvdecr(SrvFile *sf, int remove) +{ + SrvFile *f, **l; + + if(remove){ + l = &sf->dir->entry; + for(f = *l; f != nil; f = f->entry){ + if(sf == f){ + *l = f->entry; + break; + } + l = &f->entry; + } + sf->ref--; + sf->flags |= SREMOVED; + } + + if(sf->ref != 0) + return; + + if(sf->dir != nil) + srvputdir(sf->dir); + + free(sf->spec); + free(sf->user); + free(sf->name); + free(sf); +} + +static void +srvfree(SrvFile *sf, int flag) +{ + sf->flags |= flag; + if((sf->flags & (SRDCLOSE | SWRCLOSE)) == (SRDCLOSE | SWRCLOSE)){ + sf->ref--; + srvdecr(sf, (sf->flags & SREMOVED) == 0); + } +} + +static void +freerdchan(Heap *h, int swept) +{ + SrvFile *sf; + + release(); + qlock(&dev.l); + sf = H2D(Channel*, h)->aux; + sf->read = H; + srvfree(sf, SRDCLOSE); + qunlock(&dev.l); + acquire(); + freechan(h, swept); +} + +static void +freewrchan(Heap *h, int swept) +{ + SrvFile *sf; + + release(); + qlock(&dev.l); + sf = H2D(Channel*, h)->aux; + sf->write = H; + srvfree(sf, SWRCLOSE); + qunlock(&dev.l); + acquire(); + freechan(h, swept); +} + +static void +srvclunk(Chan *c, int remove) +{ + int opens, noperm; + SrvFile *sf; + + sf = c->aux; + qlock(&dev.l); + if(c->qid.type & QTDIR){ + srvputdir(sf); + qunlock(&dev.l); + if(remove) + error(Eperm); + return; + } + + opens = 0; + if(c->flag & COPEN){ + opens = sf->opens--; + if (sf->read != H || sf->write != H) + srvunblock(sf, c->fid); + } + + sf->ref--; + if(opens == 1){ + if((sf->flags & (SORCLOSE | SREMOVED)) == SORCLOSE) + remove = 1; + } + + noperm = 0; + if(remove && strcmp(sf->dir->user, up->env->user) != 0){ + noperm = 1; + remove = 0; + } + + srvdecr(sf, remove); + qunlock(&dev.l); + + if(noperm) + error(Eperm); +} + +static void +srvclose(Chan *c) +{ + srvclunk(c, 0); +} + +static void +srvremove(Chan *c) +{ + srvclunk(c, 1); +} + +static long +srvread(Chan *c, void *va, long count, vlong offset) +{ + int l; + Heap * volatile h; + Array *a; + SrvFile *sp; + Channel *rc; + Channel *rd; + Sys_Rread * volatile r; + Sys_FileIO_read req; + + if(c->qid.type & QTDIR){ + qlock(&dev.l); + if(waserror()){ + qunlock(&dev.l); + nexterror(); + } + l = devdirread(c, va, count, 0, 0, srvgen); + poperror(); + qunlock(&dev.l); + return l; + } + + sp = c->aux; + + acquire(); + if(waserror()){ + release(); + nexterror(); + } + + rd = sp->read; + if(rd == H) + error(Eshutdown); + + rc = cnewc(dev.Rread, movtmp, 1); + ptradd(D2H(rc)); + if(waserror()){ + ptrdel(D2H(rc)); + destroy(rc); + nexterror(); + } + + req.t0 = offset; + req.t1 = count; + req.t2 = c->fid; + req.t3 = rc; + csend(rd, &req); + + h = heap(dev.Rread); + r = H2D(Sys_Rread *, h); + ptradd(h); + if(waserror()){ + ptrdel(h); + destroy(r); + nexterror(); + } + + crecv(rc, r); + if(r->t1 != H) + error(string2c(r->t1)); + + a = r->t0; + l = 0; + if(a != H){ + l = a->len; + if(l > count) + l = count; + memmove(va, a->data, l); + } + + poperror(); + ptrdel(h); + destroy(r); + + poperror(); + ptrdel(D2H(rc)); + destroy(rc); + + poperror(); + release(); + + return l; +} + +static long +srvwrite(Chan *c, void *va, long count, vlong offset) +{ + long l; + Heap * volatile h; + SrvFile *sp; + Channel *wc; + Channel *wr; + Sys_Rwrite * volatile w; + Sys_FileIO_write req; + + if(c->qid.type & QTDIR) + error(Eperm); + + acquire(); + if(waserror()){ + release(); + nexterror(); + } + + sp = c->aux; + wr = sp->write; + if(wr == H) + error(Eshutdown); + + wc = cnewc(dev.Rwrite, movtmp, 1); + ptradd(D2H(wc)); + if(waserror()){ + ptrdel(D2H(wc)); + destroy(wc); + nexterror(); + } + + req.t0 = offset; + req.t1 = mem2array(va, count); + req.t2 = c->fid; + req.t3 = wc; + + ptradd(D2H(req.t1)); + if(waserror()){ + ptrdel(D2H(req.t1)); + destroy(req.t1); + nexterror(); + } + + csend(wr, &req); + + poperror(); + ptrdel(D2H(req.t1)); + destroy(req.t1); + + poperror(); + ptrdel(D2H(wc)); + destroy(wc); + + h = heap(dev.Rwrite); + w = H2D(Sys_Rwrite *, h); + ptradd(h); + if(waserror()){ + ptrdel(h); + destroy(w); + nexterror(); + } + crecv(wc, w); + if(w->t1 != H) + error(string2c(w->t1)); + poperror(); + ptrdel(h); + l = w->t0; + destroy(w); + + poperror(); + release(); + if(l < 0) + l = 0; + return l; +} + +static void +srvretype(Channel *c, SrvFile *f, Type *t) +{ + Heap *h; + + h = D2H(c); + h->t->ref--; + h->t = t; + t->ref++; + c->aux = f; +} + +int +srvf2c(char *dir, char *file, Sys_FileIO *io) +{ + SrvFile *s, *f; + volatile struct { Chan *c; } c; + + c.c = nil; + if(waserror()){ + cclose(c.c); + return -1; + } + + if(strchr(file, '/') != nil || strlen(file) >= 64 || strcmp(file, ".") == 0 || strcmp(file, "..") == 0) + error(Efilename); + + c.c = namec(dir, Aaccess, 0, 0); + if((c.c->qid.type&QTDIR) == 0 || devtab[c.c->type]->dc != 's') + error("directory not a srv device"); + + s = c.c->aux; + + qlock(&dev.l); + for(f = s->entry; f != nil; f = f->entry){ + if(strcmp(f->name, file) == 0){ + qunlock(&dev.l); + error(Eexist); + } + } + + f = malloc(sizeof(SrvFile)); + if(f == nil){ + qunlock(&dev.l); + error(Enomem); + } + + srvretype(io->read, f, Trdchan); + srvretype(io->write, f, Twrchan); + f->read = io->read; + f->write = io->write; + + kstrdup(&f->name, file); + kstrdup(&f->user, up->env->user); + f->perm = 0666 & (~0666 | (s->perm & 0666)); + f->length = 0; + f->ref = 2; + mkqid(&f->qid, dev.pathgen++, 0, QTFILE); + + f->entry = s->entry; + s->entry = f; + s->ref++; + f->dir = s; + qunlock(&dev.l); + + cclose(c.c); + poperror(); + + return 0; +} + +Dev srvdevtab = { + 's', + "srv", + + srvinit, + srvattach, + srvwalk, + srvstat, + srvopen, + devcreate, + srvclose, + srvread, + devbread, + srvwrite, + devbwrite, + srvremove, + srvwstat +}; diff --git a/emu/port/devssl.c b/emu/port/devssl.c new file mode 100644 index 00000000..2132ab9f --- /dev/null +++ b/emu/port/devssl.c @@ -0,0 +1,1443 @@ +/* + * devssl - secure sockets layer + */ +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include "mp.h" +#include "libsec.h" + +typedef struct OneWay OneWay; +struct OneWay +{ + QLock q; + QLock ctlq; + + void *state; /* encryption state */ + int slen; /* secret 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, + IDEACBC= 4, + IDEAECB= 5 +}; + +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= 1<<10, +}; + +Lock dslock; +int dshiwat; +int maxdstate = 20; +Dstate** dstate; +char** dsname; + +enum{ + Qtopdir = 1, /* top level directory */ + Qclonus, + Qconvdir, /* directory for a conversation */ + Qdata, + Qctl, + Qsecretin, + Qsecretout, + Qencalgs, + Qhashalgs +}; + +#define TYPE(x) ((ulong)(x).path & 0xf) +#define CONV(x) (((ulong)(x).path >> 4)&(Maxdstate-1)) +#define QID(c, y) (((c)<<4) | (y)) + +static char* encalgs; +static char* hashalgs; + +void producerand(void); + +static void alglistinit(void); +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 int +sslgen(Chan *c, char *dname, Dirtab *d, int nd, int s, Dir *dp) +{ + Qid q; + Dstate *ds; + char name[16], *p, *nm; + + USED(dname); + USED(nd); + USED(d); + q.type = QTFILE; + q.vers = 0; + if(s == DEVDOTDOT){ + q.path = QID(0, Qtopdir); + q.type = QTDIR; + devdir(c, q, "#D", 0, eve, 0555, dp); + return 1; + } + switch(TYPE(c->qid)) { + case Qtopdir: + 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, DMDIR|0555, dp); + return 1; + } + if(s > dshiwat) + return -1; + /* fall through */ + case Qclonus: + q.path = QID(0, Qclonus); + devdir(c, q, "clone", 0, eve, 0666, dp); + return 1; + case Qconvdir: + 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; + } + return -1; +} + +static void +sslinit(void) +{ + if((dstate = malloc(sizeof(Dstate*) * maxdstate)) == 0) + panic("sslinit"); + if((dsname = malloc(sizeof(*dsname) * maxdstate)) == 0) + panic("sslinit"); + alglistinit(); +} + +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, 0, 0, sslgen); +} + +static int +sslstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, sslgen); +} + +static Chan* +sslopen(Chan *c, int omode) +{ + Dstate *s, **pp; + int perm; + + perm = 0; + omode &= 3; + switch(omode) { + case OREAD: + perm = 4; + break; + case OWRITE: + perm = 2; + break; + case ORDWR: + perm = 6; + break; + } + + switch(TYPE(c->qid)) { + default: + panic("sslopen"); + case Qtopdir: + 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->env->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 *dp, int n) +{ + Dir d; + Dstate *s; + + n = convM2D(dp, n, &d, nil); + if(n == 0) + error(Eshortstat); + s = dstate[CONV(c->qid)]; + if(s == 0) + error(Ebadusefd); + if(strcmp(s->user, up->env->user) != 0) + error(Eperm); + if(!emptystr(d.uid)) + kstrdup(&s->user, d.uid); + if(d.mode != ~0UL) + s->perm = d.mode; + return n; +} + +static void +sslclose(Chan *c) +{ + Dstate *s; + + switch(TYPE(c->qid)) { + 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); + + sslhangup(s); + if(s->c) + cclose(s->c); + free(s->user); + free(s->in.secret); + free(s->out.secret); + free(s->in.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) + 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; + } +} + +/* + * 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); + } +} + +/* + * 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); + *l = 0; + } else { + nb = allocb(i); + memmove(nb->wp, b->rp+n, i); + nb->wp += i; + nb->next = b->next; + *l = nb; + } + b->wp -= i; + b->next = 0; + if(BLEN(b) < 0) + panic("qtake"); + return first; + } else + n -= i; + if(BLEN(b) < 0) + panic("qtake"); + } + *l = 0; + return first; +} + +static Block* +sslbread(Chan *c, long n, ulong offset) +{ + volatile struct { Dstate *s; } s; + volatile struct { int nc; } nc; + Block *b; + uchar count[3]; + int len, pad; + + USED(offset); + s.s = dstate[CONV(c->qid)]; + if(s.s == 0) + panic("sslbread"); + if(s.s->state == Sincomplete) + error(Ebadusefd); + + nc.nc = 0; + if(waserror()){ + qunlock(&s.s->in.q); + if(strcmp(up->env->errstr, "interrupted") == 0){ + if(nc.nc > 0){ + b = allocb(nc.nc); + memmove(b->wp, count, nc.nc); + b->wp += nc.nc; + b->next = s.s->unprocessed; + s.s->unprocessed = b; + } + } else + sslhangup(s.s); + nexterror(); + } + qlock(&s.s->in.q); + + if(s.s->processed == 0){ + /* read in the whole message */ + ensure(s.s, &s.s->unprocessed, 2); + + consume(&s.s->unprocessed, count, 2); + nc.nc += 2; + if(count[0] & 0x80){ + len = ((count[0] & 0x7f)<<8) | count[1]; + ensure(s.s, &s.s->unprocessed, len); + pad = 0; + } else { + len = ((count[0] & 0x3f)<<8) | count[1]; + ensure(s.s, &s.s->unprocessed, len+1); + consume(&s.s->unprocessed, count + nc.nc, 1); + pad = count[nc.nc]; + nc.nc++; + if(pad > len){ + print("pad %d buf len %d\n", pad, len); + error("bad pad in ssl message"); + } + } + nc.nc = 0; + + /* put extra on unprocessed queue */ + s.s->processed = qtake(&s.s->unprocessed, len, 0); + + if(waserror()){ + qunlock(&s.s->in.ctlq); + nexterror(); + } + qlock(&s.s->in.ctlq); + switch(s.s->state){ + case Sencrypting: + s.s->processed = decryptb(s.s, s.s->processed); + break; + case Sdigesting: + s.s->processed = pullupblock(s.s->processed, s.s->diglen); + if(s.s->processed == 0) + error("ssl message too short"); + checkdigestb(s.s, s.s->processed); + s.s->processed->rp += s.s->diglen; + break; + case Sdigenc: + s.s->processed = decryptb(s.s, s.s->processed); + s.s->processed = pullupblock(s.s->processed, s.s->diglen); + if(s.s->processed == 0) + error("ssl message too short"); + checkdigestb(s.s, s.s->processed); + s.s->processed->rp += s.s->diglen; + len -= s.s->diglen; + break; + } + s.s->in.mid++; + qunlock(&s.s->in.ctlq); + poperror(); + + /* remove pad */ + if(pad) + s.s->processed = qtake(&s.s->processed, len - pad, 1); + } + + /* return at most what was asked for */ + b = qtake(&s.s->processed, n, 0); + + qunlock(&s.s->in.q); + poperror(); + + return b; +} + +static long +sslread(Chan *c, void *a, long n, vlong offset) +{ + volatile struct { Block *b; } b; + Block *nb; + uchar *va; + int i; + char buf[128]; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, sslgen); + + switch(TYPE(c->qid)) { + default: + error(Ebadusefd); + case Qctl: + sprint(buf, "%ld", CONV(c->qid)); + return readstr(offset, a, n, buf); + case Qdata: + b.b = sslbread(c, n, offset); + break; + case Qencalgs: + return readstr(offset, a, n, encalgs); + case Qhashalgs: + return readstr(offset, a, n, hashalgs); + } + + n = 0; + va = a; + for(nb = b.b; nb; nb = nb->next){ + i = BLEN(nb); + memmove(va+n, nb->rp, i); + n += i; + } + + freeblist(b.b); + + 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++ = nrand(256); +} + +/* + * use SSL record format, add in count and digest or encrypt + */ +static long +sslbwrite(Chan *c, Block *b, ulong offset) +{ + volatile struct { Dstate *s; } s; + volatile struct { Block *b; } bb; + Block *nb; + int h, n, m, pad, rv; + uchar *p; + + bb.b = b; + s.s = dstate[CONV(c->qid)]; + if(s.s == 0) + panic("sslbwrite"); + if(s.s->state == Sincomplete){ + freeb(b); + error(Ebadusefd); + } + + if(waserror()){ + qunlock(&s.s->out.q); + if(bb.b) + freeb(bb.b); + sslhangup(s.s); + nexterror(); + } + qlock(&s.s->out.q); + + rv = 0; + while(bb.b){ + m = n = BLEN(bb.b); + h = s.s->diglen + 2; + + /* trim to maximum block size */ + pad = 0; + if(m > s.s->max){ + m = s.s->max; + } else if(s.s->blocklen != 1){ + pad = (m + s.s->diglen)%s.s->blocklen; + if(pad){ + if(m > s.s->maxpad){ + pad = 0; + m = s.s->maxpad; + } else { + pad = s.s->blocklen - pad; + h++; + } + } + } + + rv += m; + if(m != n){ + nb = allocb(m + h + pad); + memmove(nb->wp + h, bb.b->rp, m); + nb->wp += m + h; + bb.b->rp += m; + } else { + /* add header space */ + nb = padblock(bb.b, h); + bb.b = 0; + } + m += s.s->diglen; + + /* SSLv2 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.s->state){ + case Sencrypting: + nb = encryptb(s.s, nb, offset); + break; + case Sdigesting: + nb = digestb(s.s, nb, offset); + break; + case Sdigenc: + nb = digestb(s.s, nb, offset); + nb = encryptb(s.s, nb, offset); + break; + } + + s.s->out.mid++; + + m = BLEN(nb); + devtab[s.s->c->type]->bwrite(s.s->c, nb, s.s->c->offset); + s.s->c->offset += m; + } + qunlock(&s.s->out.q); + poperror(); + + return rv; +} + +static void +setsecret(OneWay *w, uchar *secret, int n) +{ + free(w->secret); + w->secret = mallocz(n, 0); + if(w->secret == nil) + error(Enomem); + memmove(w->secret, secret, n); + w->slen = n; +} + +static void +initIDEAkey(OneWay *w) +{ + + free(w->state); + w->state = malloc(sizeof(IDEAstate)); + if(w->state == nil) + error(Enomem); + if(w->slen >= 24) + setupIDEAstate(w->state, w->secret, w->secret+16); + else if(w->slen >= 16) + setupIDEAstate(w->state, w->secret, 0); + else + error("secret too short"); +} + +static void +initDESkey(OneWay *w) +{ + + free(w->state); + w->state = malloc(sizeof(DESstate)); + if (!w->state) + error(Enomem); + 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->slen >= 8) { + memmove(key, w->secret, 8); + key[0] &= 0x0f; + key[2] &= 0x0f; + key[4] &= 0x0f; + key[6] &= 0x0f; + } + + free(w->state); + w->state = malloc(sizeof(DESstate)); + if (!w->state) + error(Enomem); + 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) +{ + free(w->state); + w->state = malloc(sizeof(RC4state)); + if (!w->state) + error(Enomem); + 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) +{ + int slen = w->slen; + + if(slen > 5) + slen = 5; + + free(w->state); + w->state = malloc(sizeof(RC4state)); + if (!w->state) + error(Enomem); + setupRC4state(w->state, w->secret, 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) +{ + int slen = w->slen; + + if(slen > 16) + slen = 16; + + free(w->state); + w->state = malloc(sizeof(RC4state)); + if (!w->state) + error(Enomem); + setupRC4state(w->state, w->secret, 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*); +}; + +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, }, + { "ideacbc", 8, IDEACBC, initIDEAkey, }, + { "ideaecb", 8, IDEAECB, initIDEAkey, }, + { 0 } +}; + +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 void +alglistinit(void) +{ + Hashalg *h; + Encalg *e; + int n; + + n = 1; + for(e = encrypttab; e->name != nil; e++) + n += strlen(e->name) + 1; + encalgs = malloc(n); + if(encalgs == nil) + panic("sslinit"); + n = 0; + for(e = encrypttab; e->name != nil; e++){ + strcpy(encalgs+n, e->name); + n += strlen(e->name); + if(e[1].name == nil) + break; + encalgs[n++] = ' '; + } + encalgs[n] = 0; + + n = 1; + for(h = hashtab; h->name != nil; h++) + n += strlen(h->name) + 1; + hashalgs = malloc(n); + if(hashalgs == nil) + panic("sslinit"); + n = 0; + for(h = hashtab; h->name != nil; h++){ + strcpy(hashalgs+n, h->name); + n += strlen(h->name); + if(h[1].name == nil) + break; + hashalgs[n++] = ' '; + } + hashalgs[n] = 0; +} + +static long +sslwrite(Chan *c, void *a, long n, vlong offset) +{ + volatile struct { Dstate *s; } s; + volatile struct { Block *b; } b; + int m, t; + char *p, *np, *e, buf[32]; + uchar *x; + + s.s = dstate[CONV(c->qid)]; + if(s.s == 0) + panic("sslwrite"); + + t = TYPE(c->qid); + if(t == Qdata){ + if(s.s->state == Sincomplete) + error(Ebadusefd); + + p = a; + e = p + n; + do { + m = e - p; + if(m > s.s->max) + m = s.s->max; + + b.b = allocb(m); + memmove(b.b->wp, p, m); + b.b->wp += m; + + sslbwrite(c, b.b, offset); + + p += m; + } while(p < e); + return n; + } + + /* mutex with operations using what we're about to change */ + if(waserror()){ + qunlock(&s.s->in.ctlq); + qunlock(&s.s->out.q); + nexterror(); + } + qlock(&s.s->in.ctlq); + qlock(&s.s->out.q); + + switch(t){ + default: + panic("sslwrite"); + case Qsecretin: + setsecret(&s.s->in, a, n); + goto out; + case Qsecretout: + setsecret(&s.s->out, a, n); + goto out; + case Qctl: + break; + } + + if(n >= sizeof(buf)) + error(Ebadarg); + 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.s->c = buftochan(p); + + /* default is clear (msg delimiters only) */ + s.s->state = Sclear; + s.s->blocklen = 1; + s.s->diglen = 0; + s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1; + s.s->in.mid = 0; + s.s->out.mid = 0; + } else if(strcmp(buf, "alg") == 0 && p != 0){ + s.s->blocklen = 1; + s.s->diglen = 0; + + if(s.s->c == 0) + error("must set fd before algorithm"); + + if(strcmp(p, "clear") == 0){ + s.s->state = Sclear; + s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1; + goto out; + } + + if(s.s->in.secret && s.s->out.secret == 0) + setsecret(&s.s->out, s.s->in.secret, s.s->in.slen); + if(s.s->out.secret && s.s->in.secret == 0) + setsecret(&s.s->in, s.s->out.secret, s.s->out.slen); + if(s.s->in.secret == 0 || s.s->out.secret == 0) + error("algorithm but no secret"); + + s.s->hf = 0; + s.s->encryptalg = Noencryption; + s.s->blocklen = 1; + + for(;;){ + np = strchr(p, ' '); + if(np) + *np++ = 0; + else{ + np = strchr(p, '/'); + if(np) + *np++ = 0; + } + if(parsehashalg(p, s.s) < 0) + if(parseencryptalg(p, s.s) < 0) + error(Ebadarg); + + if(np == 0) + break; + p = np; + } + + if(s.s->hf == 0 && s.s->encryptalg == Noencryption) + error(Ebadarg); + + if(s.s->blocklen != 1){ + /* make multiple of blocklen */ + s.s->max = (1<<15) - s.s->diglen - 1; + s.s->max -= s.s->max % s.s->blocklen; + s.s->maxpad = (1<<14) - s.s->diglen - 1; + s.s->maxpad -= s.s->maxpad % s.s->blocklen; + } else + s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1; + } else if(strcmp(buf, "secretin") == 0 && p != 0) { + m = (strlen(p)*3)/2; + x = smalloc(m); + if(waserror()){ + free(x); + nexterror(); + } + t = dec64(x, m, p, strlen(p)); + setsecret(&s.s->in, x, t); + poperror(); + free(x); + } else if(strcmp(buf, "secretout") == 0 && p != 0) { + m = (strlen(p)*3)/2; + x = smalloc(m); + if(waserror()){ + free(x); + nexterror(); + } + t = dec64(x, m, p, strlen(p)); + setsecret(&s.s->out, x, t); + poperror(); + free(x); + } else + error(Ebadarg); + +out: + qunlock(&s.s->in.ctlq); + qunlock(&s.s->out.q); + poperror(); + return n; +} + + +static Block* +encryptb(Dstate *s, Block *b, int offset) +{ + uchar *p, *ep, *p2, *ip, *eip; + DESstate *ds; + IDEAstate *is; + + 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 IDEAECB: + is = s->out.state; + ep = b->rp + BLEN(b); + for(p = b->rp + offset; p < ep; p += 8) + idea_cipher(is->edkey, p, 0); + break; + case IDEACBC: + is = s->out.state; + ep = b->rp + BLEN(b); + for(p = b->rp + offset; p < ep; p += 8){ + p2 = p; + ip = is->ivec; + for(eip = ip+8; ip < eip; ) + *p2++ ^= *ip++; + idea_cipher(is->edkey, p, 0); + memmove(is->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 *inb) +{ + Block *b, **l; + uchar *p, *ep, *tp, *ip, *eip; + DESstate *ds; + IDEAstate *is; + uchar tmp[8]; + int i; + + l = &inb; + for(b = inb; 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 IDEAECB: + is = s->in.state; + ep = b->rp + BLEN(b); + for(p = b->rp; p < ep; p += 8) + idea_cipher(is->edkey, p, 1); + break; + case IDEACBC: + is = s->in.state; + ep = b->rp + BLEN(b); + for(p = b->rp; p < ep;){ + memmove(tmp, p, 8); + idea_cipher(is->edkey, p, 1); + tp = tmp; + ip = is->ivec; + for(eip = ip+8; ip < eip; ){ + *p++ ^= *ip; + *ip++ = *tp++; + } + } + break; + case RC4: + rc4(s->in.state, b->rp, BLEN(b)); + break; + } + } + return inb; +} + +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 *inb) +{ + 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 = inb; 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); + + /* requires pullupblock */ + if(memcmp(digest, inb->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(up->env->fgrp, fd, -1, 0, 1); /* error check and inc ref */ + return c; +} + +/* hang up a digest connection */ +static void +sslhangup(Dstate *s) +{ + qlock(&s->in.q); + freeblist(s->processed); + s->processed = 0; + freeblist(s->unprocessed); + s->unprocessed = 0; + s->state = Sincomplete; + qunlock(&s->in.q); +} + +static Dstate* +dsclone(Chan *ch) +{ + Dstate **pp, **ep, **np; + int newmax; + + if(waserror()) { + unlock(&dslock); + nexterror(); + } + lock(&dslock); + ep = &dstate[maxdstate]; + for(pp = dstate; pp < ep; pp++) { + if(*pp == 0) { + dsnew(ch, pp); + break; + } + } + if(pp >= ep) { + if(maxdstate >= Maxdstate) { + unlock(&dslock); + poperror(); + return 0; + } + newmax = 2 * maxdstate; + if(newmax > Maxdstate) + newmax = Maxdstate; + np = realloc(dstate, sizeof(Dstate*) * newmax); + if(np == 0) + error(Enomem); + dstate = np; + pp = &dstate[maxdstate]; + memset(pp, 0, sizeof(Dstate*)*(newmax - maxdstate)); + maxdstate = newmax; + dsnew(ch, pp); + } + unlock(&dslock); + poperror(); + return *pp; +} + +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++; + s->state = Sincomplete; + s->ref = 1; + kstrdup(&s->user, up->env->user); + s->perm = 0660; + t = TYPE(ch->qid); + if(t == Qclonus) + t = Qctl; + ch->qid.path = QID(pp - dstate, t); + ch->qid.vers = 0; + ch->qid.type = QTFILE; +} + +Dev ssldevtab = { + 'D', + "ssl", + + sslinit, + sslattach, + sslwalk, + sslstat, + sslopen, + devcreate, + sslclose, + sslread, + sslbread, + sslwrite, + sslbwrite, + devremove, + sslwstat +}; diff --git a/emu/port/devtinyfs.c b/emu/port/devtinyfs.c new file mode 100644 index 00000000..33689745 --- /dev/null +++ b/emu/port/devtinyfs.c @@ -0,0 +1,895 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + + +enum{ + Qdir, + Qmedium, + + Maxfs= 10, /* max file systems */ + + Blen= 48, /* block length */ + Nlen= 28, /* name length */ + Dlen= Blen - 4, + + Tagdir= 'd', + Tagdata= 'D', + Tagend= 'e', + Tagfree= 'f', + + Notapin= 0xffff, + Notabno= 0xffff, + + Fcreating= 1, + Frmonclose= 2 +}; + +/* representation of a Tdir on medium */ +typedef struct Mdir Mdir; +struct Mdir { + uchar type; + uchar bno[2]; + uchar pin[2]; + char name[Nlen]; + char pad[Blen - Nlen - 6]; + uchar sum; +}; + +/* representation of a Tdata/Tend on medium */ +typedef struct Mdata Mdata; +struct Mdata { + uchar type; + uchar bno[2]; + uchar data[Dlen]; + uchar sum; +}; + +typedef struct Tfile Tfile; +struct Tfile { + int r; + char name[Nlen]; + ushort bno; + ushort dbno; + ushort pin; + uchar flag; + ulong length; + + /* hint to avoid egregious reading */ + ushort fbno; + ulong finger; +}; + +typedef struct Tfs Tfs; +struct Tfs { + QLock ql; + int r; + Chan *c; + uchar *map; + int nblocks; + Tfile *f; + int nf; + int fsize; +}; + +static struct { + Tfs fs[Maxfs]; +} tinyfs; + +#define GETS(x) ((x)[0]|((x)[1]<<8)) +#define PUTS(x, v) {(x)[0] = (v);(x)[1] = ((v)>>8);} + +#define GETL(x) (GETS(x)|(GETS(x+2)<<16)) +#define PUTL(x, v) {PUTS(x, v);PUTS(x+2, (v)>>16)}; + +static uchar +checksum(uchar *p) +{ + uchar *e; + uchar s; + + s = 0; + for(e = p + Blen; p < e; p++) + s += *p; + return s; +} + +static void +mapclr(Tfs *fs, ulong bno) +{ + fs->map[bno>>3] &= ~(1<<(bno&7)); +} + +static void +mapset(Tfs *fs, ulong bno) +{ + fs->map[bno>>3] |= 1<<(bno&7); +} + +static int +isalloced(Tfs *fs, ulong bno) +{ + return fs->map[bno>>3] & (1<<(bno&7)); +} + +static int +mapalloc(Tfs *fs) +{ + int i, j, lim; + uchar x; + + lim = (fs->nblocks + 8 - 1)/8; + for(i = 0; i < lim; i++){ + x = fs->map[i]; + if(x == 0xff) + continue; + for(j = 0; j < 8; j++) + if((x & (1<<j)) == 0){ + fs->map[i] = x|(1<<j); + return i*8 + j; + } + } + + return Notabno; +} + +static Mdir* +validdir(Tfs *fs, uchar *p) +{ + Mdir *md; + ulong x; + + if(checksum(p) != 0) + return 0; + if(p[0] != Tagdir) + return 0; + md = (Mdir*)p; + x = GETS(md->bno); + if(x >= fs->nblocks) + return 0; + return md; +} + +static Mdata* +validdata(Tfs *fs, uchar *p, int *lenp) +{ + Mdata *md; + ulong x; + + if(checksum(p) != 0) + return 0; + md = (Mdata*)p; + switch(md->type){ + case Tagdata: + x = GETS(md->bno); + if(x >= fs->nblocks) + return 0; + if(lenp) + *lenp = Dlen; + break; + case Tagend: + x = GETS(md->bno); + if(x > Dlen) + return 0; + if(lenp) + *lenp = x; + break; + default: + return 0; + } + return md; +} + +static Mdata* +readdata(Tfs *fs, ulong bno, uchar *buf, int *lenp) +{ + if(bno >= fs->nblocks) + return 0; + if(devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno) != Blen) + error(Eio); + return validdata(fs, buf, lenp); +} + +static void +writedata(Tfs *fs, ulong bno, ulong next, uchar *buf, int len, int last) +{ + Mdata md; + + if(bno >= fs->nblocks) + error(Eio); + if(len > Dlen) + len = Dlen; + if(len < 0) + error(Eio); + memset(&md, 0, sizeof(md)); + if(last){ + md.type = Tagend; + PUTS(md.bno, len); + } else { + md.type = Tagdata; + PUTS(md.bno, next); + } + memmove(md.data, buf, len); + md.sum = 0 - checksum((uchar*)&md); + + if(devtab[fs->c->type]->write(fs->c, &md, Blen, Blen*bno) != Blen) + error(Eio); +} + +static void +writedir(Tfs *fs, Tfile *f) +{ + Mdir *md; + uchar buf[Blen]; + + if(f->bno == Notabno) + return; + + md = (Mdir*)buf; + memset(buf, 0, Blen); + md->type = Tagdir; + strncpy(md->name, f->name, sizeof(md->name)-1); + PUTS(md->bno, f->dbno); + PUTS(md->pin, f->pin); + md->sum = 0 - checksum(buf); + + if(devtab[fs->c->type]->write(fs->c, buf, Blen, Blen*f->bno) != Blen) + error(Eio); +} + +static void +freeblocks(Tfs *fs, ulong bno, ulong bend) +{ + uchar buf[Blen]; + Mdata *md; + + if(waserror()) + return; + + while(bno != bend && bno != Notabno){ + mapclr(fs, bno); + if(devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno) != Blen) + break; + md = validdata(fs, buf, 0); + if(md == 0) + break; + if(md->type == Tagend) + break; + bno = GETS(md->bno); + } + + poperror(); +} + +static void +freefile(Tfs *fs, Tfile *f, ulong bend) +{ + uchar buf[Blen]; + + /* remove blocks from map */ + freeblocks(fs, f->dbno, bend); + + /* change file type to free on medium */ + if(f->bno != Notabno){ + memset(buf, 0x55, Blen); + devtab[fs->c->type]->write(fs->c, buf, Blen, Blen*f->bno); + mapclr(fs, f->bno); + } + + /* forget we ever knew about it */ + memset(f, 0, sizeof(*f)); +} + +static void +expand(Tfs *fs) +{ + Tfile *f; + + fs->fsize += 8; + f = malloc(fs->fsize*sizeof(*f)); + if(f == nil) + error(Enomem); + + if(fs->f){ + memmove(f, fs->f, fs->nf*sizeof(*f)); + free(fs->f); + } + fs->f = f; +} + +static Tfile* +newfile(Tfs *fs, char *name) +{ + int i; + volatile struct { + Tfile *f; + Tfs *fs; + } rock; + + /* find free entry in file table */ + rock.f = 0; + rock.fs = fs; + for(;;) { + for(i = 0; i < rock.fs->fsize; i++){ + rock.f = &rock.fs->f[i]; + if(rock.f->name[0] == 0){ + strncpy(rock.f->name, name, sizeof(rock.f->name)-1); + break; + } + } + + if(i < rock.fs->fsize){ + if(i >= rock.fs->nf) + rock.fs->nf = i+1; + break; + } + + expand(rock.fs); + } + + rock.f->flag = Fcreating; + rock.f->dbno = Notabno; + rock.f->bno = mapalloc(rock.fs); + rock.f->fbno = Notabno; + rock.f->r = 1; + rock.f->pin = up->env->pgrp->pin; + + /* write directory block */ + if(waserror()){ + freefile(rock.fs, rock.f, Notabno); + nexterror(); + } + if(rock.f->bno == Notabno) + error("out of space"); + writedir(rock.fs, rock.f); + poperror(); + + return rock.f; +} + +/* + * Read the whole medium and build a file table and used + * block bitmap. Inconsistent files are purged. The medium + * had better be small or this could take a while. + */ +static void +tfsinit(Tfs *fs) +{ + uchar dbuf[STATFIXLEN+32*4]; + Dir d; + uchar buf[Blen]; + ulong x, bno; + int n, done; + Tfile *f; + Mdir *mdir; + Mdata *mdata; + + n = devtab[fs->c->type]->stat(fs->c, dbuf, sizeof(dbuf)); + n = convM2D(dbuf, n, &d, nil); + if(n <= 0) + error("cannot stat tinyfs medium"); + fs->nblocks = d.length/Blen; + if(fs->nblocks < 3) + error("tinyfs medium too small"); + + /* bitmap for block usage */ + x = (fs->nblocks + 8 - 1)/8; + fs->map = malloc(x); + if(fs->map == nil) + error(Enomem); + for(bno = fs->nblocks; bno < x*8; bno++) + mapset(fs, bno); + + /* find files */ + for(bno = 0; bno < fs->nblocks; bno++){ + n = devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno); + if(n != Blen) + break; + + mdir = validdir(fs, buf); + if(mdir == 0) + continue; + + if(fs->nf >= fs->fsize) + expand(fs); + + f = &fs->f[fs->nf++]; + + x = GETS(mdir->bno); + mapset(fs, bno); + strncpy(f->name, mdir->name, sizeof(f->name)); + f->pin = GETS(mdir->pin); + f->bno = bno; + f->dbno = x; + f->fbno = Notabno; + } + + /* follow files */ + for(f = fs->f; f < &(fs->f[fs->nf]); f++){ + bno = f->dbno; + for(done = 0; !done;) { + if(isalloced(fs, bno)){ + freefile(fs, f, bno); + break; + } + n = devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno); + if(n != Blen){ + freefile(fs, f, bno); + break; + } + mdata = validdata(fs, buf, 0); + if(mdata == 0){ + freefile(fs, f, bno); + break; + } + mapset(fs, bno); + switch(mdata->type){ + case Tagdata: + bno = GETS(mdata->bno); + f->length += Dlen; + break; + case Tagend: + f->length += GETS(mdata->bno); + done = 1; + break; + } + if(done) + f->flag &= ~Fcreating; + } + } +} + +/* + * single directory + */ +static int +tinyfsgen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp) +{ + Tfs *fs; + Tfile *f; + Qid qid; + + USED(name); + USED(ntab); + USED(tab); + + fs = &tinyfs.fs[c->dev]; + if(i >= fs->nf) + return -1; + qid.vers = 0; + if(i == DEVDOTDOT){ + qid.type = QTDIR; + devdir(c, qid, ".", 0, eve, DMDIR|0555, dp); + return 1; + } + f = &fs->f[i]; + if(f->name[0] == 0) + return 0; + qid.path = i+1; + qid.type = QTFILE; + devdir(c, qid, f->name, f->length, eve, 0664, dp); + return 1; +} + +/* + * specifier is the name of a device in /dev + */ +static Chan * +tinyfsattach(char *spec) +{ + Tfs *fs; + Chan *c; + volatile struct { Chan *cc; } rock; + int i; + char buf[KNAMELEN*3]; + + if(*spec == 0 || strchr(spec, '/') != nil) + error("bad specifier"); + + snprint(buf, sizeof(buf), "/dev/%s", spec); + rock.cc = namec(buf, Aopen, ORDWR, 0); + if(waserror()){ + cclose(rock.cc); + nexterror(); + } + + fs = 0; + for(i = 0; i < Maxfs; i++){ + fs = &tinyfs.fs[i]; + qlock(&fs->ql); + if(fs->r && eqchan(rock.cc, fs->c, 1)) + break; + qunlock(&fs->ql); + } + if(i < Maxfs){ + fs->r++; + qunlock(&fs->ql); + cclose(rock.cc); + } else { + for(fs = tinyfs.fs; fs < &tinyfs.fs[Maxfs]; fs++){ + qlock(&fs->ql); + if(fs->r == 0) + break; + qunlock(&fs->ql); + } + if(fs == &tinyfs.fs[Maxfs]) + error("too many tinyfs's"); + fs->c = rock.cc; + fs->r = 1; + fs->f = 0; + fs->nf = 0; + fs->fsize = 0; + tfsinit(fs); + qunlock(&fs->ql); + } + poperror(); + + c = devattach('F', spec); + c->dev = fs - tinyfs.fs; + c->qid.type = QTDIR; + c->qid.vers = 0; + + return c; +} + +static Walkqid* +tinyfswalk(Chan *c, Chan *nc, char **name, int nname) +{ + Tfs *fs; + Walkqid *wq; + + fs = &tinyfs.fs[c->dev]; + + qlock(&fs->ql); + wq = devwalk(c, nc, name, nname, 0, 0, tinyfsgen); + if(wq != nil && (nc = wq->clone) != nil && nc->qid.path != Qdir){ + fs = &tinyfs.fs[nc->dev]; + fs->f[nc->qid.path-1].r++; + } + qunlock(&fs->ql); + return wq; +} + +static int +tinyfsstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, tinyfsgen); +} + +static Chan * +tinyfsopen(Chan *c, int omode) +{ + Tfile *f; + volatile struct { Tfs *fs; } rock; + + rock.fs = &tinyfs.fs[c->dev]; + + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Eperm); + } else { + qlock(&rock.fs->ql); + if(waserror()){ + qunlock(&rock.fs->ql); + nexterror(); + } + switch(omode){ + case OTRUNC|ORDWR: + case OTRUNC|OWRITE: + f = newfile(rock.fs, rock.fs->f[c->qid.path-1].name); + rock.fs->f[c->qid.path-1].r--; + c->qid.path = f - rock.fs->f; + break; + case OREAD: + break; + default: + error(Eperm); + } + qunlock(&rock.fs->ql); + poperror(); + } + + return devopen(c, omode, 0, 0, tinyfsgen); +} + +static void +tinyfscreate(Chan *c, char *name, int omode, ulong perm) +{ + volatile struct { Tfs *fs; } rock; + Tfile *f; + + USED(perm); + + rock.fs = &tinyfs.fs[c->dev]; + + qlock(&rock.fs->ql); + if(waserror()){ + qunlock(&rock.fs->ql); + nexterror(); + } + f = newfile(rock.fs, name); + qunlock(&rock.fs->ql); + poperror(); + + c->qid.path = (f - rock.fs->f)+1; + c->qid.vers = 0; + c->qid.type = QTFILE; + c->mode = openmode(omode); +} + +static void +tinyfsremove(Chan *c) +{ + Tfs *fs; + Tfile *f; + + if(c->qid.path == Qdir) + error(Eperm); + fs = &tinyfs.fs[c->dev]; + f = &fs->f[c->qid.path-1]; + qlock(&fs->ql); + freefile(fs, f, Notabno); + qunlock(&fs->ql); +} + +static void +tinyfsclose(Chan *c) +{ + volatile struct { Tfs *fs; } rock; + Tfile *f, *nf; + int i; + + rock.fs = &tinyfs.fs[c->dev]; + + qlock(&rock.fs->ql); + + /* dereference file and remove old versions */ + if(!waserror()){ + if(c->qid.path != Qdir){ + f = &rock.fs->f[c->qid.path-1]; + f->r--; + if(f->r == 0){ + if(f->flag & Frmonclose) + freefile(rock.fs, f, Notabno); + else if(f->flag & Fcreating){ + /* remove all other files with this name */ + for(i = 0; i < rock.fs->fsize; i++){ + nf = &rock.fs->f[i]; + if(f == nf) + continue; + if(strcmp(nf->name, f->name) == 0){ + if(nf->r) + nf->flag |= Frmonclose; + else + freefile(rock.fs, nf, Notabno); + } + } + f->flag &= ~Fcreating; + } + } + } + poperror(); + } + + /* dereference rock.fs and remove on zero refs */ + rock.fs->r--; + if(rock.fs->r == 0){ + if(rock.fs->f) + free(rock.fs->f); + rock.fs->f = 0; + rock.fs->nf = 0; + rock.fs->fsize = 0; + if(rock.fs->map) + free(rock.fs->map); + rock.fs->map = 0; + cclose(rock.fs->c); + rock.fs->c = 0; + } + qunlock(&rock.fs->ql); +} + +static long +tinyfsread(Chan *c, void *a, long n, vlong offset) +{ + volatile struct { Tfs *fs; } rock; + Tfile *f; + int sofar, i, off; + ulong bno; + Mdata *md; + uchar buf[Blen]; + uchar *p; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, tinyfsgen); + + p = a; + rock.fs = &tinyfs.fs[c->dev]; + f = &rock.fs->f[c->qid.path-1]; + if(offset >= f->length) + return 0; + + qlock(&rock.fs->ql); + if(waserror()){ + qunlock(&rock.fs->ql); + nexterror(); + } + if(n + offset >= f->length) + n = f->length - offset; + + /* walk to starting data block */ + if(0 && f->finger <= offset && f->fbno != Notabno){ + sofar = f->finger; + bno = f->fbno; + } else { + sofar = 0; + bno = f->dbno; + } + for(; sofar + Dlen <= offset; sofar += Dlen){ + md = readdata(rock.fs, bno, buf, 0); + if(md == 0) + error(Eio); + bno = GETS(md->bno); + } + + /* read data */ + off = offset%Dlen; + offset -= off; + for(sofar = 0; sofar < n; sofar += i){ + md = readdata(rock.fs, bno, buf, &i); + if(md == 0) + error(Eio); + + /* update finger for successful read */ + f->finger = offset; + f->fbno = bno; + offset += Dlen; + + i -= off; + if(i > n - sofar) + i = n - sofar; + memmove(p, md->data+off, i); + p += i; + bno = GETS(md->bno); + off = 0; + } + qunlock(&rock.fs->ql); + poperror(); + + return sofar; +} + +/* + * if we get a write error in this routine, blocks will + * be lost. They should be recovered next fsinit. + */ +static long +tinyfswrite(Chan *c, void *a, long n, vlong offset) +{ + Tfile *f; + int last, next, i, finger, off, used; + ulong bno, fbno; + Mdata *md; + uchar buf[Blen]; + uchar *p; + volatile struct { + Tfs *fs; + ulong dbno; + } rock; + + if(c->qid.type & QTDIR) + error(Eperm); + + if(n == 0) + return 0; + + p = a; + rock.fs = &tinyfs.fs[c->dev]; + f = &rock.fs->f[c->qid.path-1]; + + qlock(&rock.fs->ql); + rock.dbno = Notabno; + if(waserror()){ + freeblocks(rock.fs, rock.dbno, Notabno); + qunlock(&rock.fs->ql); + nexterror(); + } + + /* files are append only, anything else is illegal */ + if(offset != f->length) + error("append only"); + + /* write blocks backwards */ + p += n; + last = offset + n; + fbno = Notabno; + finger = 0; + off = offset; /* so we have something signed to compare against */ + for(next = ((last-1)/Dlen)*Dlen; next >= off; next -= Dlen){ + bno = mapalloc(rock.fs); + if(bno == Notabno) + error("out of space"); + i = last - next; + p -= i; + if(last == n+offset){ + writedata(rock.fs, bno, rock.dbno, p, i, 1); + finger = next; /* remember for later */ + fbno = bno; + } else { + writedata(rock.fs, bno, rock.dbno, p, i, 0); + } + rock.dbno = bno; + last = next; + } + + /* walk to last data block */ + md = (Mdata*)buf; + if(0 && f->finger < offset && f->fbno != Notabno){ + next = f->finger; + bno = f->fbno; + } else { + next = 0; + bno = f->dbno; + } + + used = 0; + while(bno != Notabno){ + md = readdata(rock.fs, bno, buf, &used); + if(md == 0) + error(Eio); + if(md->type == Tagend){ + if(next + Dlen < offset) + panic("devtinyfs1"); + break; + } + next += Dlen; + if(next > offset) + panic("devtinyfs1"); + bno = GETS(md->bno); + } + + /* point to new blocks */ + if(offset == 0){ + /* first block in a file */ + f->dbno = rock.dbno; + writedir(rock.fs, f); + } else { + /* updating a current block */ + i = last - offset; + if(i > 0){ + p -= i; + memmove(md->data + used, p, i); + used += i; + } + writedata(rock.fs, bno, rock.dbno, md->data, used, last == n+offset); + } + f->length += n; + + /* update finger */ + if(fbno != Notabno){ + f->finger = finger; + f->fbno = fbno; + } + poperror(); + qunlock(&rock.fs->ql); + + return n; +} + +Dev tinyfsdevtab = { + 'F', + "tinyfs", + + devinit, + tinyfsattach, + tinyfswalk, + tinyfsstat, + tinyfsopen, + tinyfscreate, + tinyfsclose, + tinyfsread, + devbread, + tinyfswrite, + devbwrite, + tinyfsremove, + devwstat +}; diff --git a/emu/port/devtk.c b/emu/port/devtk.c new file mode 100644 index 00000000..6f31967b --- /dev/null +++ b/emu/port/devtk.c @@ -0,0 +1,177 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include <interp.h> + +#include "image.h" +#include <memimage.h> +#include <memlayer.h> +#include <cursor.h> + +enum{ + Qdir, + Qtkevents +}; + +static +Dirtab tkdirtab[]={ + "tkevents", {Qtkevents, 0}, 0, 0600, +}; + +static struct { + QLock l; + Queue* eq; + Ref inuse; +} tkevents; + +static void +tkwiretapper(void *top, char *cmd, char *result, void *image, Rectangle *rp) +{ + Block *b; + int n; + char *s, *e; + + n = 12; + if(cmd != nil) + n += strlen(cmd)+2+1; + if(result != nil) + n += strlen(result)+2+1; + if(image != nil) + n += 12; + if(rp != nil) + n += 4*20; + n++; + b = allocb(n); + if(b != nil){ + s = (char*)b->wp; + e = s+n; + s += snprint(s, e-s, "%p", top); + if(cmd != nil){ + *s++ = ' '; + *s++ = '['; + n = strlen(cmd); + memmove(s, cmd, n); + s += n; + *s++ = ']'; + } + /* ignore result for now */ + if(image != nil) + s += snprint(s, e-s, " %p", image); + if(rp != nil) + s += snprint(s, e-s, " %d %d %d %d", rp->min.x, rp->min.y, rp->max.x, rp->max.y); + b->wp = (uchar*)s; + release(); + qlock(&tkevents.l); + if(waserror()){ + freeb(b); + qunlock(&tkevents.l); + acquire(); + return; + } + if(tkevents.eq != nil) + qbwrite(tkevents.eq, b); + qunlock(&tkevents.l); + acquire(); + } +} + +void (*tkwiretap)(void*, char*, char*, void*, Rectangle*) = tkwiretapper; + +static Chan* +tkattach(char* spec) +{ + return devattach(L'τ', spec); +} + +static int +tkwalk(Chan* c, char* name) +{ + return devwalk(c, name, tkdirtab, nelem(tkdirtab), devgen); +} + +static void +tkstat(Chan* c, char* db) +{ + devstat(c, db, tkdirtab, nelem(tkdirtab), devgen); +} + +static Chan* +tkopen(Chan* c, int omode) +{ + if(c->qid.type & QTDIR) + return devopen(c, omode, tkdirtab, nelem(tkdirtab), devgen); + switch(c->qid.path){ + case Qtkevents: + c = devopen(c, omode, tkdirtab, nelem(tkdirtab), devgen); + qlock(&tkevents.l); + if(incref(&tkevents.inuse) != 1){ + qunlock(&tkevents.l); + error(Einuse); + } + if(tkevents.eq == nil) + tkevents.eq = qopen(256*1024, 0, nil, nil); + else + qreopen(tkevents.eq); + qunlock(&tkevents.l); + break; + } + return c; +} + +static void +tkclose(Chan* c) +{ + if(c->qid.type & QTDIR || (c->flag & COPEN) == 0) + return; + qlock(&tkevents.l); + if(decref(&tkevents.inuse) == 0) + qclose(tkevents.eq); + qunlock(&tkevents.l); +} + +static long +tkread(Chan* c, void* a, long n, vlong offset) +{ + USED(offset); + switch(c->qid.path & ~CHDIR){ + case Qdir: + return devdirread(c, a, n, tkdirtab, nelem(tkdirtab), devgen); + case Qtkevents: + return qread(tkevents.eq, a, n); + default: + n=0; + break; + } + return n; +} + +static long +tkwrite(Chan *c, void* a, long n, vlong offset) +{ + USED(c); USED(a); USED(n); USED(offset); + error(Ebadusefd); + return 0; +} + +Dev tkdevtab = { + L'τ', + "tk", + +// devreset, + devinit, + tkattach, +// devdetach, + devclone, + tkwalk, + tkstat, + tkopen, + devcreate, + tkclose, + tkread, + devbread, + tkwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/emu/port/dial.c b/emu/port/dial.c new file mode 100644 index 00000000..97930c16 --- /dev/null +++ b/emu/port/dial.c @@ -0,0 +1,414 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "kernel.h" + +typedef struct DS DS; + +static int call(char*, char*, DS*); +static int csdial(DS*); +static void _dial_string_parse(char*, DS*); +static int nettrans(char*, char*, int na, char*, int); + +enum +{ + Maxstring= 128, + Maxpath= 100 +}; + +struct DS +{ + char buf[Maxstring]; /* dist string */ + char *netdir; + char *proto; + char *rem; + char *local; /* other args */ + char *dir; + int *cfdp; +}; + +/* + * the dialstring is of the form '[/net/]proto!dest' + */ +int +kdial(char *dest, char *local, char *dir, int *cfdp) +{ + DS ds; + int rv; + char err[ERRMAX], alterr[ERRMAX]; + + ds.local = local; + ds.dir = dir; + ds.cfdp = cfdp; + + _dial_string_parse(dest, &ds); + if(ds.netdir) + return csdial(&ds); + + ds.netdir = "/net"; + rv = csdial(&ds); + if(rv >= 0) + return rv; + + err[0] = 0; + kerrstr(err, sizeof err); + if(strstr(err, "refused") != 0){ + kerrstr(err, sizeof err); + return rv; + } + + ds.netdir = "/net.alt"; + rv = csdial(&ds); + if(rv >= 0) + return rv; + + alterr[0] = 0; + kerrstr(alterr, sizeof err); + + if(strstr(alterr, "translate") || strstr(alterr, "does not exist")) + kerrstr(err, sizeof err); + else + kerrstr(alterr, sizeof alterr); + return rv; +} + +static int +csdial(DS *ds) +{ + int n, fd, rv; + char *p, buf[Maxstring], clone[Maxpath], err[ERRMAX], besterr[ERRMAX]; + + /* + * open connection server + */ + snprint(buf, sizeof(buf), "%s/cs", ds->netdir); + fd = kopen(buf, ORDWR); + if(fd < 0){ + /* no connection server, don't translate */ + snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto); + return call(clone, ds->rem, ds); + } + + /* + * ask connection server to translate + */ + sprint(buf, "%s!%s", ds->proto, ds->rem); + if(kwrite(fd, buf, strlen(buf)) < 0){ + kerrstr(err, sizeof err); + kclose(fd); + kwerrstr("%s (%s)", err, buf); + return -1; + } + + /* + * loop through each address from the connection server till + * we get one that works. + */ + *besterr = 0; + strcpy(err, Egreg); + rv = -1; + kseek(fd, 0, 0); + while((n = kread(fd, buf, sizeof(buf) - 1)) > 0){ + buf[n] = 0; + p = strchr(buf, ' '); + if(p == 0) + continue; + *p++ = 0; + rv = call(buf, p, ds); + if(rv >= 0) + break; + err[0] = 0; + kerrstr(err, sizeof err); + if(strstr(err, "does not exist") == 0) + memmove(besterr, err, sizeof besterr); + } + kclose(fd); + + if(rv < 0 && *besterr) + kerrstr(besterr, sizeof besterr); + else + kerrstr(err, sizeof err); + return rv; +} + +static int +call(char *clone, char *dest, DS *ds) +{ + int fd, cfd, n; + char name[Maxpath], data[Maxpath], err[ERRMAX], *p; + + cfd = kopen(clone, ORDWR); + if(cfd < 0){ + kerrstr(err, sizeof err); + kwerrstr("%s (%s)", err, clone); + return -1; + } + + /* get directory name */ + n = kread(cfd, name, sizeof(name)-1); + if(n < 0){ + kerrstr(err, sizeof err); + kclose(cfd); + kwerrstr("read %s: %s", clone, err); + return -1; + } + name[n] = 0; + for(p = name; *p == ' '; p++) + ; + sprint(name, "%ld", strtoul(p, 0, 0)); + p = strrchr(clone, '/'); + *p = 0; + if(ds->dir) + snprint(ds->dir, NETPATHLEN, "%s/%s", clone, name); + snprint(data, sizeof(data), "%s/%s/data", clone, name); + + /* connect */ + if(ds->local) + snprint(name, sizeof(name), "connect %s %s", dest, ds->local); + else + snprint(name, sizeof(name), "connect %s", dest); + if(kwrite(cfd, name, strlen(name)) < 0){ + err[0] = 0; + kerrstr(err, sizeof err); + kclose(cfd); + kwerrstr("%s (%s)", err, name); + return -1; + } + + /* open data connection */ + fd = kopen(data, ORDWR); + if(fd < 0){ + err[0] = 0; + kerrstr(err, sizeof err); + kwerrstr("%s (%s)", err, data); + kclose(cfd); + return -1; + } + if(ds->cfdp) + *ds->cfdp = cfd; + else + kclose(cfd); + + return fd; +} + +/* + * parse a dial string + */ +static void +_dial_string_parse(char *str, DS *ds) +{ + char *p, *p2; + + strncpy(ds->buf, str, Maxstring); + ds->buf[Maxstring-1] = 0; + + p = strchr(ds->buf, '!'); + if(p == 0) { + ds->netdir = 0; + ds->proto = "net"; + ds->rem = ds->buf; + } else { + if(*ds->buf != '/' && *ds->buf != '#'){ + ds->netdir = 0; + ds->proto = ds->buf; + } else { + for(p2 = p; *p2 != '/'; p2--) + ; + *p2++ = 0; + ds->netdir = ds->buf; + ds->proto = p2; + } + *p = 0; + ds->rem = p + 1; + } +} + +/* + * announce a network service. + */ +int +kannounce(char *addr, char *dir) +{ + int ctl, n, m; + char buf[NETPATHLEN]; + char buf2[Maxpath]; + char netdir[NETPATHLEN]; + char naddr[Maxpath]; + char *cp; + + /* + * translate the address + */ + if(nettrans(addr, naddr, sizeof(naddr), netdir, sizeof(netdir)) < 0) + return -1; + + /* + * get a control channel + */ + ctl = kopen(netdir, ORDWR); + if(ctl<0) + return -1; + cp = strrchr(netdir, '/'); + *cp = 0; + + /* + * find out which line we have + */ + n = sprint(buf, "%.*s/", sizeof buf, netdir); + m = kread(ctl, &buf[n], sizeof(buf)-n-1); + if(m <= 0){ + kclose(ctl); + return -1; + } + buf[n+m] = 0; + + /* + * make the call + */ + n = snprint(buf2, sizeof buf2, "announce %s", naddr); + if(kwrite(ctl, buf2, n)!=n){ + kclose(ctl); + return -1; + } + + /* + * return directory etc. + */ + if(dir) + strcpy(dir, buf); + return ctl; +} + +/* + * listen for an incoming call + */ +int +klisten(char *dir, char *newdir) +{ + int ctl, n, m; + char buf[NETPATHLEN]; + char *cp; + + /* + * open listen, wait for a call + */ + snprint(buf, sizeof buf, "%s/listen", dir); + ctl = kopen(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 = kread(ctl, cp, sizeof(buf) - n - 1); + if(m <= 0){ + kclose(ctl); + return -1; + } + buf[n+m] = 0; + + /* + * return directory etc. + */ + if(newdir) + strcpy(newdir, buf); + return ctl; + +} + +/* + * perform the identity translation (in case we can't reach cs) + */ +static int +identtrans(char *netdir, char *addr, char *naddr, int na, char *file, int nf) +{ + char proto[Maxpath]; + char *p; + + USED(nf); + + /* parse the protocol */ + strncpy(proto, addr, sizeof(proto)); + proto[sizeof(proto)-1] = 0; + p = strchr(proto, '!'); + if(p) + *p++ = 0; + + snprint(file, nf, "%s/%s/clone", netdir, proto); + strncpy(naddr, p, na); + naddr[na-1] = 0; + + return 1; +} + +/* + * call up the connection server and get a translation + */ +static int +nettrans(char *addr, char *naddr, int na, char *file, int nf) +{ + int i, fd; + char buf[Maxpath]; + char netdir[NETPATHLEN]; + char *p, *p2; + long n; + + /* + * parse, get network directory + */ + p = strchr(addr, '!'); + if(p == 0){ + kwerrstr("bad dial string: %s", addr); + return -1; + } + if(*addr != '/'){ + strcpy(netdir, "/net"); + } else { + for(p2 = p; *p2 != '/'; p2--) + ; + i = p2 - addr; + if(i == 0 || i >= sizeof(netdir)){ + kwerrstr("bad dial string: %s", addr); + return -1; + } + strncpy(netdir, addr, i); + netdir[i] = 0; + addr = p2 + 1; + } + + /* + * ask the connection server + */ + sprint(buf, "%s/cs", netdir); + fd = kopen(buf, ORDWR); + if(fd < 0) + return identtrans(netdir, addr, naddr, na, file, nf); + if(kwrite(fd, addr, strlen(addr)) < 0){ + kclose(fd); + return -1; + } + kseek(fd, 0, 0); + n = kread(fd, buf, sizeof(buf)-1); + kclose(fd); + if(n <= 0) + return -1; + buf[n] = 0; + + /* + * parse the reply + */ + p = strchr(buf, ' '); + if(p == 0) + return -1; + *p++ = 0; + strncpy(naddr, p, na); + naddr[na-1] = 0; + strncpy(file, buf, nf); + file[nf-1] = 0; + return 0; +} diff --git a/emu/port/dis.c b/emu/port/dis.c new file mode 100644 index 00000000..05feeb02 --- /dev/null +++ b/emu/port/dis.c @@ -0,0 +1,1125 @@ +#include "dat.h" +#include "fns.h" +#include <isa.h> +#include <interp.h> +#include <kernel.h> +#include "error.h" +#include "raise.h" + +struct +{ + Lock l; + Prog* runhd; + Prog* runtl; + Prog* head; + Prog* tail; + Rendez irend; + int idle; + int nyield; + int creating; + Proc* vmq; /* queue of procs wanting vm */ + Proc* vmqt; + Proc* idlevmq; /* queue of procs wanting work */ + Atidle* idletasks; +} isched; + +int bflag; +int cflag; +uvlong gcbusy; +uvlong gcidle; +uvlong gcidlepass; +uvlong gcpartial; +int keepbroken = 1; +extern int vflag; +static Prog* proghash[64]; + +static Progs* delgrp(Prog*); +static void addgrp(Prog*, Prog*); +void printgrp(Prog*, char*); + +static Prog** +pidlook(int pid) +{ + ulong h; + Prog **l; + + h = (ulong)pid % nelem(proghash); + for(l = &proghash[h]; *l != nil && (*l)->pid != pid; l = &(*l)->pidlink) + ; + return l; +} + +int +tready(void *a) +{ + USED(a); + return isched.runhd != nil || isched.vmq != nil; +} + +Prog* +progpid(int pid) +{ + return *pidlook(pid); +} + +Prog* +progn(int n) +{ + Prog *p; + + for(p = isched.head; p && n--; p = p->next) + ; + return p; +} + +int +nprog(void) +{ + int n; + Prog *p; + + n = 0; + for(p = isched.head; p; p = p->next) + n++; + return n; +} + +static void +execatidle(void) +{ + int done; + + if(tready(nil)) + return; + + gcidle++; + up->type = IdleGC; + up->iprog = nil; + addrun(up->prog); + done = gccolor+3; + while(gccolor < done && gcruns()) { + if(isched.vmq != nil || isched.runhd != isched.runtl) { + gcpartial++; + break; + } + rungc(isched.head); + gcidlepass++; + osyield(); + } + up->type = Interp; + delrunq(up->prog); +} + +Prog* +newprog(Prog *p, Modlink *m) +{ + Heap *h; + Prog *n, **ph; + Osenv *on, *op; + static int pidnum; + + n = malloc(sizeof(Prog)+sizeof(Osenv)); + if(n == 0){ + if(p == nil) + panic("no memory"); + else + error(exNomem); + } + + n->pid = ++pidnum; + if(n->pid <= 0) + panic("no pids"); + n->group = nil; + + if(isched.tail != nil) { + n->prev = isched.tail; + isched.tail->next = n; + } + else { + isched.head = n; + n->prev = nil; + } + isched.tail = n; + + ph = pidlook(n->pid); + if(*ph != nil) + panic("dup pid"); + n->pidlink = nil; + *ph = n; + + n->osenv = (Osenv*)((uchar*)n + sizeof(Prog)); + n->xec = xec; + n->quanta = PQUANTA; + n->flags = 0; + n->exval = H; + + h = D2H(m); + h->ref++; + Setmark(h); + n->R.M = m; + n->R.MP = m->MP; + if(m->MP != H) + Setmark(D2H(m->MP)); + addrun(n); + + if(p == nil){ + newgrp(n); + return n; + } + + addgrp(n, p); + n->flags = p->flags; + if(p->flags & Prestrict) + n->flags |= Prestricted; + memmove(n->osenv, p->osenv, sizeof(Osenv)); + op = p->osenv; + on = n->osenv; + on->waitq = op->childq; + on->childq = nil; + on->debug = nil; + incref(&on->pgrp->r); + incref(&on->fgrp->r); + incref(&on->egrp->r); + if(on->sigs != nil) + incref(&on->sigs->r); + on->user = nil; + kstrdup(&on->user, op->user); + on->errstr = on->errbuf0; + on->syserrstr = on->errbuf1; + + return n; +} + +void +delprog(Prog *p, char *msg) +{ + Osenv *o; + Prog **ph; + + tellsomeone(p, msg); /* call before being removed from prog list */ + + o = p->osenv; + release(); + closepgrp(o->pgrp); + closefgrp(o->fgrp); + closeegrp(o->egrp); + closesigs(o->sigs); + acquire(); + + delgrp(p); + + if(p->prev) + p->prev->next = p->next; + else + isched.head = p->next; + + if(p->next) + p->next->prev = p->prev; + else + isched.tail = p->prev; + + ph = pidlook(p->pid); + if(*ph == nil) + panic("lost pid"); + *ph = p->pidlink; + + if(p == isched.runhd) { + isched.runhd = p->link; + if(p->link == nil) + isched.runtl = nil; + } + p->state = 0xdeadbeef; + free(o->user); + if(p->killstr) + free(p->killstr); + if(p->exstr) + free(p->exstr); + free(p); +} + +void +renameproguser(char *old, char *new) +{ + Prog *p; + Osenv *o; + + acquire(); + for(p = isched.head; p; p = p->next){ + o = p->osenv; + if(o->user != nil && strcmp(o->user, old) == 0) + kstrdup(&o->user, new); + } + release(); +} + +void +tellsomeone(Prog *p, char *buf) +{ + Osenv *o; + + if(waserror()) + return; + o = p->osenv; + if(o->childq != nil) + qproduce(o->childq, buf, strlen(buf)); + if(o->waitq != nil) + qproduce(o->waitq, buf, strlen(buf)); + poperror(); +} + +static void +swiprog(Prog *p) +{ + Proc *q; + + lock(&procs.l); + for(q = procs.head; q; q = q->next) { + if(q->iprog == p) { + unlock(&procs.l); + swiproc(q, 1); + return; + } + } + unlock(&procs.l); + /*print("didn't find\n");*/ +} + +static Prog* +grpleader(Prog *p) +{ + Progs *g; + Prog *l; + + g = p->group; + if(g != nil && (l = g->head) != nil && l->pid == g->id) + return l; + return nil; +} + +int +exprog(Prog *p, char *exc) +{ + /* similar code to killprog but not quite */ + switch(p->state) { + case Palt: + altdone(p->R.s, p, nil, -1); + break; + case Psend: + cqdelp(&p->chan->send, p); + break; + case Precv: + cqdelp(&p->chan->recv, p); + break; + case Pready: + break; + case Prelease: + swiprog(p); + break; + case Pexiting: + case Pbroken: + case Pdebug: + return 0; + default: + panic("exprog - bad state 0x%x\n", p->state); + } + if(p->state != Pready && p->state != Prelease) + addrun(p); + if(p->kill == nil){ + if(p->killstr == nil){ + p->killstr = malloc(ERRMAX); + if(p->killstr == nil){ + p->kill = Enomem; + return 1; + } + } + kstrcpy(p->killstr, exc, ERRMAX); + p->kill = p->killstr; + } + return 1; +} + +static void +propex(Prog *p, char *estr) +{ + Prog *f, *nf, *pgl; + + if(!(p->flags & (Ppropagate|Pnotifyleader)) || p->group == nil) + return; + if(*estr == 0){ + if((p->flags & Pkilled) == 0) + return; + estr = "killed"; + } + pgl = grpleader(p); + if(pgl == nil) + pgl = p; + if(!(pgl->flags & (Ppropagate|Pnotifyleader))) + return; /* exceptions are local; don't propagate */ + for(f = p->group->head; f != nil; f = nf){ + nf = f->grpnext; + if(f != p && f != pgl){ + if(pgl->flags & Ppropagate) + exprog(f, estr); + else{ + f->flags &= ~(Ppropagate|Pnotifyleader); /* prevent recursion */ + killprog(f, "killed"); + } + } + } + if(p != pgl) + exprog(pgl, estr); +} + +int +killprog(Prog *p, char *cause) +{ + Osenv *env; + char msg[ERRMAX+2*KNAMELEN]; + + if(p == isched.runhd) { + p->kill = ""; + p->flags |= Pkilled; + p->state = Pexiting; + return 0; + } + + switch(p->state) { + case Palt: + altdone(p->R.s, p, nil, -1); + break; + case Psend: + cqdelp(&p->chan->send, p); + break; + case Precv: + cqdelp(&p->chan->recv, p); + break; + case Pready: + delrunq(p); + break; + case Prelease: + p->kill = ""; + p->flags |= Pkilled; + p->state = Pexiting; + swiprog(p); + /* No break */ + case Pexiting: + return 0; + case Pbroken: + case Pdebug: + break; + default: + panic("killprog - bad state 0x%x\n", p->state); + } + + if(p->addrun != nil) { + p->kill = ""; + p->flags |= Pkilled; + p->addrun(p); + p->addrun = nil; + return 0; + } + + env = p->osenv; + if(env->debug != nil) { + p->state = Pbroken; + dbgexit(p, 0, cause); + return 0; + } + + propex(p, "killed"); + + snprint(msg, sizeof(msg), "%d \"%s\":%s", p->pid, p->R.M->m->name, cause); + + p->state = Pexiting; + gclock(); + destroystack(&p->R); + delprog(p, msg); + gcunlock(); + + return 1; +} + +void +newgrp(Prog *p) +{ + Progs *pg, *g; + + if(p->group != nil && p->group->id == p->pid) + return; + g = malloc(sizeof(*g)); + if(g == nil) + error(Enomem); + p->flags &= ~(Ppropagate|Pnotifyleader); + g->id = p->pid; + g->flags = 0; + g->child = nil; + pg = delgrp(p); + g->head = g->tail = p; + p->group = g; + if(pg != nil){ + g->sib = pg->child; + pg->child = g; + } + g->parent = pg; +} + +static void +addgrp(Prog *n, Prog *p) +{ + Progs *g; + + n->group = p->group; + if((g = n->group) != nil){ + n->grpnext = nil; + if(g->head != nil){ + n->grpprev = g->tail; + g->tail->grpnext = n; + }else{ + n->grpprev = nil; + g->head = n; + } + g->tail = n; + } +} + +static Progs* +delgrp(Prog *p) +{ + Progs *g, *pg, *cg, **l; + + g = p->group; + if(g == nil) + return nil; + if(p->grpprev) + p->grpprev->grpnext = p->grpnext; + else + g->head = p->grpnext; + if(p->grpnext) + p->grpnext->grpprev = p->grpprev; + else + g->tail = p->grpprev; + p->grpprev = p->grpnext = nil; + p->group = nil; + + if(g->head == nil){ + /* move up, giving subgroups of groups with no Progs to their parents */ + do{ + if((pg = g->parent) != nil){ + pg = g->parent; + for(l = &pg->child; *l != nil && *l != g; l = &(*l)->sib) + ; + *l = g->sib; + } + /* put subgroups in new parent group */ + while((cg = g->child) != nil){ + g->child = cg->sib; + cg->parent = pg; + if(pg != nil){ + cg->sib = pg->child; + pg->child = cg; + } + } + free(g); + }while((g = pg) != nil && g->head == nil); + } + return g; +} + +void +printgrp(Prog *p, char *v) +{ + Progs *g; + Prog *q; + + g = p->group; + print("%s pid %d grp %d pgrp %d: [pid", v, p->pid, g->id, g->parent!=nil?g->parent->id:0); + for(q = g->head; q != nil; q = q->grpnext) + print(" %d", q->pid); + print(" subgrp"); + for(g = g->child; g != nil; g = g->sib) + print(" %d", g->id); + print("]\n"); +} + +int +killgrp(Prog *p, char *msg) +{ + int i, npid, *pids; + Prog *f; + Progs *g; + + /* interpreter has been acquired */ + g = p->group; + if(g == nil || g->head == nil) + return 0; + npid = 0; + for(f = g->head; f != nil; f = f->grpnext) + if(f->group != g) + panic("killgrp"); + else + npid++; + /* use pids not Prog* because state can change during killprog */ + pids = malloc(npid*sizeof(int)); + if(pids == nil) + error(Enomem); + npid = 0; + for(f = g->head; f != nil; f = f->grpnext) + pids[npid++] = f->pid; + if(waserror()) { + free(pids); + nexterror(); + } + for(i = 0; i < npid; i++) { + f = progpid(pids[i]); + if(f != nil && f != currun()) + killprog(f, msg); + } + poperror(); + free(pids); + return 1; +} + +char changup[] = "channel hangup"; + +void +killcomm(Progq **q) +{ + Prog *p; + Progq *f; + + for (f = *q; f != nil; f = *q) { + *q = f->next; + p = f->prog; + free(f); + if(p == nil) + return; + p->ptr = nil; + switch(p->state) { + case Prelease: + swiprog(p); + break; + case Psend: + case Precv: + p->kill = changup; + addrun(p); + break; + case Palt: + altgone(p); + break; + } + } +} + +void +addprog(Proc *p) +{ + Prog *n; + + n = malloc(sizeof(Prog)); + if(n == nil) + panic("no memory"); + p->prog = n; + n->osenv = p->env; +} + +static void +cwakeme(Prog *p) +{ + Osenv *o; + + p->addrun = nil; + o = p->osenv; + Wakeup(o->rend); +} + +static int +cdone(void *vp) +{ + Prog *p = vp; + + return p->addrun == nil || p->kill != nil; +} + +void +cblock(Prog *p) +{ + Osenv *o; + + p->addrun = cwakeme; + o = p->osenv; + o->rend = &up->sleep; + release(); + + /* + * To allow cdone(p) safely after release, + * p must be currun before the release. + * Exits in the error case with the vm acquired. + */ + if(waserror()) { + acquire(); + p->addrun = nil; + nexterror(); + } + Sleep(o->rend, cdone, p); + if (p->kill != nil) + error(Eintr); + poperror(); + acquire(); +} + +void +addrun(Prog *p) +{ + if(p->addrun != 0) { + p->addrun(p); + return; + } + p->state = Pready; + p->link = nil; + if(isched.runhd == nil) + isched.runhd = p; + else + isched.runtl->link = p; + + isched.runtl = p; +} + +Prog* +delrun(int state) +{ + Prog *p; + + p = isched.runhd; + p->state = state; + isched.runhd = p->link; + if(p->link == nil) + isched.runtl = nil; + + return p; +} + +void +delrunq(Prog *p) +{ + Prog *prev, *f; + + prev = nil; + for(f = isched.runhd; f; f = f->link) { + if(f == p) + break; + prev = f; + } + if(f == nil) + return; + if(prev == nil) + isched.runhd = p->link; + else + prev->link = p->link; + if(p == isched.runtl) + isched.runtl = prev; +} + +Prog* +delruntail(int state) +{ + Prog *p; + + p = isched.runtl; + delrunq(p); + p->state = state; + return p; +} + +Prog* +currun(void) +{ + return isched.runhd; +} + +Prog* +schedmod(Module *m) +{ + Heap *h; + Type *t; + Prog *p; + Modlink *ml; + Frame f, *fp; + + ml = mklinkmod(m, 0); + + if(m->origmp != H && m->ntype > 0) { + t = m->type[0]; + h = nheap(t->size); + h->t = t; + t->ref++; + ml->MP = H2D(uchar*, h); + newmp(ml->MP, m->origmp, t); + } + + p = newprog(nil, ml); + h = D2H(ml); + h->ref--; + p->R.PC = m->entry; + fp = &f; + R.s = &fp; + f.t = m->entryt; + newstack(p); + initmem(m->entryt, p->R.FP); + + return p; +} + +/* +static char* +m(Prog *p) +{ + if(p) + if(p->R.M) + if(p->R.M->m) + return p->R.M->m->name; + return "nil"; +} +*/ + +void +acquire(void) +{ + int empty; + Prog *p; + + lock(&isched.l); + if(isched.idle) { + isched.idle = 0; + unlock(&isched.l); + } + else { + up->qnext = nil; + if(isched.vmq != nil){ + empty = 0; + isched.vmqt->qnext = up; + }else{ + isched.vmq = up; + empty = 1; + } + isched.vmqt = up; + + unlock(&isched.l); + strcpy(up->text, "acquire"); + if(empty) + Wakeup(&isched.irend); + osblock(); + } + + if(up->type == Interp) { + p = up->iprog; + up->iprog = nil; + irestore(p); + } + else + p = up->prog; + + p->state = Pready; + p->link = isched.runhd; + isched.runhd = p; + if(p->link == nil) + isched.runtl = p; + + strcpy(up->text, "dis"); +} + +void +release(void) +{ + Proc *p, **pq; + int f; + + if(up->type == Interp) + up->iprog = isave(); + else + delrun(Prelease); + + lock(&isched.l); + if(*(pq = &isched.vmq) == nil && *(pq = &isched.idlevmq) == nil) { + isched.idle = 1; + f = isched.creating; + isched.creating = 1; + unlock(&isched.l); + if(f == 0) + kproc("dis", vmachine, nil, 0); + return; + } + p = *pq; + *pq = p->qnext; + unlock(&isched.l); + + osready(p); /* wake up thread to run VM */ + strcpy(up->text, "released"); +} + +void +iyield(void) +{ + Proc *p; + + lock(&isched.l); + p = isched.vmq; + if(p == nil) { + unlock(&isched.l); + return; + } + isched.nyield++; + isched.vmq = p->qnext; + + if(up->iprog != nil) + panic("iyield but iprog, type %d", up->type); + if(up->type != Interp){ + static int once; + if(!once++) + print("tell charles: #%p->type==%d\n", up, up->type); + } + up->qnext = isched.idlevmq; + isched.idlevmq = up; + + unlock(&isched.l); + osready(p); /* wake up acquiring kproc */ + strcpy(up->text, "yield"); + osblock(); /* sleep */ + strcpy(up->text, "dis"); +} + +void +startup(void) +{ + + up->type = Interp; + up->iprog = nil; + + lock(&isched.l); + isched.creating = 0; + if(isched.idle) { + isched.idle = 0; + unlock(&isched.l); + return; + } + up->qnext = isched.idlevmq; + isched.idlevmq = up; + unlock(&isched.l); + + osblock(); +} + +void +progexit(void) +{ + Prog *r; + Module *m; + int broken; + char *estr, msg[ERRMAX+2*KNAMELEN]; + + estr = up->env->errstr; + broken = 0; + if(estr[0] != '\0' && strcmp(estr, Eintr) != 0 && strncmp(estr, "fail:", 5) != 0) + broken = 1; + + r = up->iprog; + if(r != nil) + acquire(); + else + r = currun(); + + if(*estr == '\0' && r->flags & Pkilled) + estr = "killed"; + + m = R.M->m; + if(broken){ + if(cflag){ /* only works on Plan9 for now */ + char *pc = strstr(estr, "pc="); + + if(pc != nil) + R.PC = r->R.PC = (Inst*)strtol(pc+3, nil, 0); /* for debugging */ + } + print("[%s] Broken: \"%s\"\n", m->name, estr); + } + + snprint(msg, sizeof(msg), "%d \"%s\":%s", r->pid, m->name, estr); + + if(up->env->debug != nil) { + dbgexit(r, broken, estr); + broken = 1; + /* must force it to break if in debug */ + }else if(broken && (!keepbroken || strncmp(estr, "out of memory", 13)==0 || memusehigh())) + broken = 0; /* don't want them or short of memory */ + + if(broken){ + tellsomeone(r, msg); + r = isave(); + r->state = Pbroken; + return; + } + + gclock(); + destroystack(&R); + delprog(r, msg); + gcunlock(); + + if(isched.head == nil) + cleanexit(0); +} + +void +disfault(void *reg, char *msg) +{ + Prog *p; + + USED(reg); + + if(strncmp(msg, Eintr, 6) == 0) + exits(0); + + if(up == nil) { + print("EMU: faults: %s\n", msg); + cleanexit(0); + } + if(up->type != Interp) { + print("SYS: process %s faults: %s\n", up->text, msg); + cleanexit(0); + } + + if(up->iprog != nil) + acquire(); + + p = currun(); + if(p == nil) + panic("Interp faults with no dis prog"); + + /* cause an exception in the dis prog. As for error(), but Plan 9 needs reg*/ + kstrcpy(up->env->errstr, msg, ERRMAX); + oslongjmp(reg, up->estack[--up->nerr], 1); +} + +void +vmachine(void *a) +{ + Prog *r; + Osenv *o; + int cycles; + static int gccounter; + + USED(a); + + startup(); + + while(waserror()) { + if(up->iprog != nil) + acquire(); + if(handler(up->env->errstr) == 0) { + propex(currun(), up->env->errstr); + progexit(); + } + up->env = &up->defenv; + } + + cycles = 0; + for(;;) { + if(tready(nil) == 0) { + execatidle(); + strcpy(up->text, "idle"); + Sleep(&isched.irend, tready, 0); + strcpy(up->text, "dis"); + } + + if(isched.vmq != nil && (isched.runhd == nil || ++cycles > 2)){ + iyield(); + cycles = 0; + } + + r = isched.runhd; + if(r != nil) { + o = r->osenv; + up->env = o; + + FPrestore(&o->fpu); + r->xec(r); + FPsave(&o->fpu); + + if(isched.runhd != nil) + if(r == isched.runhd) + if(isched.runhd != isched.runtl) { + isched.runhd = r->link; + r->link = nil; + isched.runtl->link = r; + isched.runtl = r; + } + up->env = &up->defenv; + } + if(isched.runhd != nil) + if((++gccounter&0xFF) == 0 || memlow()) { + gcbusy++; + up->type = BusyGC; + pushrun(up->prog); + rungc(isched.head); + up->type = Interp; + delrunq(up->prog); + } + } +} + +void +disinit(void *a) +{ + Prog *p; + Osenv *o; + Module *root; + char *initmod = a; + + if(waserror()) + panic("disinit error: %r"); + + if(vflag) + print("Initial Dis: \"%s\"\n", initmod); + + fmtinstall('D', Dconv); + + FPinit(); + FPsave(&up->env->fpu); + + opinit(); + modinit(); + excinit(); + + root = load(initmod); + if(root == 0) { + kgerrstr(up->genbuf, sizeof up->genbuf); + panic("loading \"%s\": %s", initmod, up->genbuf); + } + + p = schedmod(root); + + memmove(p->osenv, up->env, sizeof(Osenv)); + o = p->osenv; + incref(&o->pgrp->r); + incref(&o->fgrp->r); + incref(&o->egrp->r); + if(o->sigs != nil) + incref(&o->sigs->r); + o->user = nil; + kstrdup(&o->user, up->env->user); + o->errstr = o->errbuf0; + o->syserrstr = o->errbuf1; + + isched.idle = 1; + poperror(); + vmachine(nil); +} + +void +pushrun(Prog *p) +{ + if(p->addrun != nil) + panic("pushrun addrun"); + p->state = Pready; + p->link = isched.runhd; + isched.runhd = p; + if(p->link == nil) + isched.runtl = p; +} diff --git a/emu/port/discall.c b/emu/port/discall.c new file mode 100644 index 00000000..56470d77 --- /dev/null +++ b/emu/port/discall.c @@ -0,0 +1,179 @@ +#include "dat.h" +#include "fns.h" +#include <interp.h> + +#define QP(l) (Prog**)((char*)(l)+sizeof(QLock)) + +void* +libqlowner(void *l) +{ + return *QP(l); +} + +void +libqlock(void *l) +{ + Prog *p; + QLock *q; + + q = l; + p = currun(); + if(p == nil) + abort(); + + if(!canqlock(q)) { + release(); + qlock(q); + acquire(); + } + *QP(l) = p; +} + +void +libqunlock(void *l) +{ + Prog *p; + QLock *q; + + q = l; + p = currun(); + if(p == nil) + abort(); + if(*QP(l) != p) + abort(); + + *QP(l) = nil; + qunlock(q); +} + +void* +libqlalloc(void) +{ + QLock *q; + + q = mallocz(sizeof(QLock)+sizeof(Prog*), 1); + return q; +} + +void +libqlfree(void *l) +{ + free(l); +} + +vlong +libseek(int fd, vlong off, int whence) +{ + release(); + off = kseek(fd, off, whence); + acquire(); + return off; +} + +int +libread(int fd, void *buf, int n) +{ + release(); + n = kread(fd, buf, n); + acquire(); + return n; +} + +int +libreadn(int fd, void *av, long n) +{ + char *a; + long m, t; + + a = av; + t = 0; + release(); + while(t < n){ + m = kread(fd, a+t, n-t); + if(m <= 0){ + if(t == 0){ + acquire(); + return m; + } + break; + } + t += m; + } + acquire(); + return t; +} + +int +libwrite(int fd, void *buf, int n) +{ + release(); + n = kwrite(fd, buf, n); + acquire(); + return n; +} + +int +libopen(char *name, int omode) +{ + int fd; + + release(); + fd = kopen(name, omode); + acquire(); + return fd; +} + +int +libclose(int fd) +{ + release(); + fd = kclose(fd); + acquire(); + return fd; +} + +Dir* +libdirfstat(int fd) +{ + Dir *d; + + release(); + d = kdirfstat(fd); + acquire(); + return d; +} + +int +libbind(char *s, char *t, int f) +{ + int n; + + release(); + n = kbind(s, t, f); + acquire(); + return n; +} + +void +libchanclose(void *chan) +{ + release(); + cclose(chan); + acquire(); +} + +void* +libfdtochan(int fd, int mode) +{ + Chan *c; + + release(); + if(waserror()) { + acquire(); + return nil; + } + c = fdtochan(up->env->fgrp, fd, mode, 0, 1); + poperror(); + acquire(); + return c; +} diff --git a/emu/port/dynld.c b/emu/port/dynld.c new file mode 100644 index 00000000..6c5bf8e4 --- /dev/null +++ b/emu/port/dynld.c @@ -0,0 +1,52 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include <a.out.h> +#include <dynld.h> + +/* + * kernel interface to dynld, for use by devdynld.c, + * libinterp/dlm.c, and possibly others + */ + +typedef struct Fd Fd; +struct Fd { + int fd; +}; + +static long +readfd(void *a, void *buf, long nbytes) +{ + return kread(((Fd*)a)->fd, buf, nbytes); +} + +static vlong +seekfd(void *a, vlong off, int t) +{ + return kseek(((Fd*)a)->fd, off, t); +} + +static void +errfd(char *s) +{ + kstrcpy(up->env->errstr, s, ERRMAX); +} + +Dynobj* +kdynloadfd(int fd, Dynsym *tab, int ntab) +{ + Dynobj *o; + Fd f; + + f.fd = fd; + return dynloadgen(&f, readfd, seekfd, errfd, tab, ntab, 0); +} + +int +kdynloadable(int fd) +{ + Fd f; + + f.fd = fd; + return dynloadable(&f, readfd, seekfd); +} diff --git a/emu/port/dynldc.c b/emu/port/dynldc.c new file mode 100644 index 00000000..2237f7ab --- /dev/null +++ b/emu/port/dynldc.c @@ -0,0 +1,65 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include <a.out.h> +#include <dynld.h> + +/* + * channel-based kernel interface to dynld, for use by devdynld.c, + * libinterp/dlm.c, and possibly others + */ + +static long +readfc(void *a, void *buf, long nbytes) +{ + Chan *c = a; + + if(waserror()) + return -1; + nbytes = devtab[c->type]->read(c, buf, nbytes, c->offset); + poperror(); + return nbytes; +} + +static vlong +seekfc(void *a, vlong off, int t) +{ + Chan *c = a; + + if(c->qid.type & QTDIR || off < 0) + return -1; /* won't happen */ + switch(t){ + case 0: + lock(c); + c->offset = off; + unlock(c); + break; + case 1: + lock(c); + off += c->offset; + c->offset = off; + unlock(c); + break; + case 2: + return -1; /* not needed */ + } + return off; +} + +static void +errfc(char *s) +{ + kstrcpy(up->env->errstr, s, ERRMAX); +} + +Dynobj* +kdynloadchan(Chan *c, Dynsym *tab, int ntab) +{ + return dynloadgen(c, readfc, seekfc, errfc, tab, ntab, 0); +} + +int +kdynloadable(Chan *c) +{ + return dynloadable(c, readfc, seekfc); +} diff --git a/emu/port/env.c b/emu/port/env.c new file mode 100644 index 00000000..36ca5636 --- /dev/null +++ b/emu/port/env.c @@ -0,0 +1,77 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +Egrp* +newegrp(void) +{ + Egrp *e; + + e = smalloc(sizeof(Egrp)); + if (e == nil) + error(Enomem); + e->r.ref = 1; + return e; +} + +void +closeegrp(Egrp *e) +{ + Evalue *el, *nl; + + if(e == nil || decref(&e->r) != 0) + return; + for (el = e->entries; el != nil; el = nl) { + free(el->var); + if (el->val) + free(el->val); + nl = el->next; + free(el); + } + free(e); +} + +void +egrpcpy(Egrp *to, Egrp *from) +{ + Evalue *e, *ne, **last; + + last = &to->entries; + qlock(&from->l); + for (e = from->entries; e != nil; e = e->next) { + ne = smalloc(sizeof(Evalue)); + ne->var = smalloc(strlen(e->var)+1); + strcpy(ne->var, e->var); + if (e->val) { + ne->val = smalloc(e->len); + memmove(ne->val, e->val, e->len); + ne->len = e->len; + } + ne->qid.path = ++to->path; + *last = ne; + last = &ne->next; + } + qunlock(&from->l); +} + +void +ksetenv(char *var, char *val, int conf) +{ + Chan *c; + char buf[2*KNAMELEN]; + + USED(conf); + snprint(buf, sizeof(buf), "#e/%s", var); + if(waserror()) + return; + c = namec(buf, Acreate, OWRITE, 0600); + poperror(); + if(!waserror()){ + if(!waserror()){ + devtab[c->type]->write(c, val, strlen(val), 0); + poperror(); + } + poperror(); + } + cclose(c); +} diff --git a/emu/port/error.c b/emu/port/error.c new file mode 100644 index 00000000..ad1342fc --- /dev/null +++ b/emu/port/error.c @@ -0,0 +1,66 @@ +char Enoerror[] = "no error"; +char Emount[] = "inconsistent mount"; +char Eunmount[] = "not mounted"; +char Eunion[] = "not in union"; +char Emountrpc[] = "mount rpc error"; +char Eshutdown[] = "mounted device shut down"; +char Eowner[] = "not owner"; +char Eunknown[] = "unknown user or group id"; +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 Enetaddr[] = "bad network address"; +char Emsgsize[] = "message is too big for protocol"; +char Enetbusy[] = "network device is busy or allocated"; +char Enoproto[] = "network protocol not supported"; +char Enoport[] = "network port not available"; +char Enoifc[] = "bad interface or no free interface slots"; +char Enolisten[] = "not announced"; +char Ehungup[] = "i/o on hungup channel"; +char Ebadctl[] = "bad process or channel control request"; +char Enodev[] = "no free devices"; +char Enoenv[] = "no free environment resources"; +char Emuxshutdown[] = "mux server shut down"; +char Emuxbusy[] = "all mux channels busy"; +char Emuxmsg[] = "bad mux message format or mismatch"; +char Ethread[] = "thread exited"; +char Enochild[] = "no living children"; +char Eioload[] = "i/o error in demand load"; +char Enovmem[] = "out of memory: virtual memory"; +char Ebadld[] = "illegal line discipline"; +char Ebadfd[] = "fd out of range or not open"; +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 Enetunreach[] = "network unreachable"; +char Eintr[] = "interrupted"; +char Enomem[] = "out of memory: kernel"; +char Esfnotcached[] = "subfont not cached"; +char Esoverlap[] = "segments overlap"; +char Emouseset[] = "mouse type already set"; +char Eshort[] = "i/o count too small"; +/* char Enobitstore[] = "out of screen memory"; */ +char Egreg[] = "jim'll fix it"; +char Ebadspec[] = "bad attach specifier"; +char Estopped[] = "thread must be stopped"; +char Enoattach[] = "mount/attach disallowed"; +char Eshortstat[] = "stat buffer too small"; +char Enegoff[] = "negative i/o offset"; +char Ebadstat[] = "malformed stat buffer"; +char Ecmdargs[] = "wrong #args in control message"; +char Enofd[] = "no free file descriptors"; +char Enoctl[] = "unknown control request"; diff --git a/emu/port/error.h b/emu/port/error.h new file mode 100644 index 00000000..b1911397 --- /dev/null +++ b/emu/port/error.h @@ -0,0 +1,68 @@ +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[]; /* mounted device shut down */ +extern char Eowner[]; /* not owner */ +extern char Eunknown[]; /* unknown user or group id */ +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 Enetaddr[]; /* bad network address */ +extern char Emsgsize[]; /* message is too big for protocol */ +extern char Enetbusy[]; /* network device is busy or allocated */ +extern char Enoproto[]; /* network protocol not supported */ +extern char Enoport[]; /* network port not available */ +extern char Enoifc[]; /* bad interface or no free interface slots */ +extern char Enolisten[]; /* not announced */ +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 Enoenv[]; /* no free environment resources */ +extern char Emuxshutdown[]; /* mux server shut down */ +extern char Emuxbusy[]; /* all mux channels busy */ +extern char Emuxmsg[]; /* bad mux message format or mismatch */ +extern char Ethread[]; /* thread exited */ +extern char Enochild[]; /* no living children */ +extern char Eioload[]; /* i/o error in demand load */ +extern char Enovmem[]; /* out of memory: virtual memory */ +extern char Ebadld[]; /* illegal line discipline */ +extern char Ebadfd[]; /* fd out of range or not open */ +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 Enetunreach[]; /* network unreachable */ +extern char Eintr[]; /* interrupted */ +extern char Enomem[]; /* out of memory: kernel */ +extern char Esfnotcached[]; /* subfont not cached */ +extern char Esoverlap[]; /* segments overlap */ +extern char Emouseset[]; /* mouse type already set */ +extern char Eshort[]; /* i/o count too small */ +extern char Enobitstore[]; /* out of screen memory */ +extern char Egreg[]; /* jim'll fix it */ +extern char Ebadspec[]; /* bad attach specifier */ +extern char Estopped[]; /* thread must be stopped */ +extern char Enoattach[]; /* mount/attach disallowed */ +extern char Eshortstat[]; /* stat buffer too small */ +extern char Enegoff[]; /* negative i/o offset */ +extern char Ebadstat[]; /* malformed stat buffer */ +extern char Ecmdargs[]; /* wrong #args in control message */ +extern char Enofd[]; /* no free file descriptors */ +extern char Enoctl[]; /* unknown control request */ + +extern void error(char*); diff --git a/emu/port/errstr.c b/emu/port/errstr.c new file mode 100644 index 00000000..dfb41ed4 --- /dev/null +++ b/emu/port/errstr.c @@ -0,0 +1,38 @@ +#include "dat.h" +#include "fns.h" + +/* + * General OS interface to errors + */ +void +kwerrstr(char *fmt, ...) +{ + va_list arg; + char buf[ERRMAX]; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + kstrcpy(up->env->errstr, buf, ERRMAX); +} + +void +kerrstr(char *err, uint size) +{ + char tmp[ERRMAX]; + + kstrcpy(tmp, up->env->errstr, sizeof(tmp)); + kstrcpy(up->env->errstr, err, ERRMAX); + kstrcpy(err, tmp, size); +} + +void +kgerrstr(char *err, uint size) +{ + char *s; + + s = "<no-up>"; + if(up != nil) + s = up->env->errstr; + kstrcpy(err, s, size); /* TO DO */ +} diff --git a/emu/port/exception.c b/emu/port/exception.c new file mode 100644 index 00000000..8808ed56 --- /dev/null +++ b/emu/port/exception.c @@ -0,0 +1,214 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "interp.h" +#include "isa.h" +#include "runt.h" +#include "kernel.h" +#include "raise.h" + +static int +ematch(char *pat, char *exp) +{ + int l; + + if(strcmp(pat, exp) == 0) + return 1; + + l = strlen(pat); + if(l == 0) + return 0; + if(pat[l-1] == '*') { + if(l == 1) + return 1; + if(strncmp(pat, exp, l-1) == 0) + return 1; + } + return 0; +} + +static void +setstr(String *s, char *p) +{ + if(s == H) + return; + if(s->len < 0 || s->max < 4) + return; + kstrcpy(s->Sascii, p, s->max); /* TO DO: we are assuming they aren't runes */ + s->len = strlen(s->Sascii); +} + +static String *exstr; + +void +excinit(void) +{ + exstr = newstring(ERRMAX); + poolimmutable(D2H(exstr)); +} + +static String* +newestring(char *estr) +{ + String *s; + + if(waserror()){ + setstr(exstr, estr); + D2H(exstr)->ref++; + return exstr; + } + s = c2string(estr, strlen(estr)); + poperror(); + return s; +} + +#define NOPC 0xffffffff + +#define FRTYPE(f) ((f)->t == nil ? SEXTYPE(f)->reg.TR : (f)->t) + +/* + * clear up an uncalled frame + */ +static void +freeframe(uchar *fp, int setsp) +{ + Frame *f; + + f = (Frame*)fp; + if(f->t == nil) + unextend(f); + else if(f->t->np) + freeptrs(f, f->t); + if(setsp) + R.SP = fp; +} + +int +handler(char *estr) +{ + Prog *p; + Modlink *m, *mr; + int str, ne; + ulong pc, newpc; + long eoff; + uchar *fp, **eadr; + Frame *f; + Type *t, *zt; + Handler *h; + Except *e; + void *v; + + p = currun(); + if(*estr == 0 || p == nil) + return 0; + str = p->exval == H || D2H(p->exval)->t == &Tstring; + m = R.M; + if(m->compiled) + pc = (ulong)R.PC-(ulong)m->prog; + else + pc = R.PC-m->prog; + pc--; + fp = R.FP; + + while(fp != nil){ /* look for a handler */ + if((h = m->m->htab) != nil){ + for( ; h->etab != nil; h++){ + if(pc < h->pc1 || pc >= h->pc2) + continue; + eoff = h->eoff; + zt = h->t; + for(e = h->etab, ne = h->ne; e->s != nil; e++, ne--){ + if(ematch(e->s, estr) && (str && ne <= 0 || !str && ne > 0)){ + newpc = e->pc; + goto found; + } + } + newpc = e->pc; + if(newpc != NOPC) + goto found; + } + } + if(!str && fp != R.FP){ /* becomes a string exception in immediate caller */ + v = p->exval; + p->exval = *(String**)v; + D2H(p->exval)->ref++; + destroy(v); + str = 1; + continue; + } + f = (Frame*)fp; + if(f->mr != nil) + m = f->mr; + if(m->compiled) + pc = (ulong)f->lr-(ulong)m->prog; + else + pc = f->lr-m->prog; + pc--; + fp = f->fp; + } + destroy(p->exval); + p->exval = H; + return 0; +found: + { + int n; + char name[3*KNAMELEN]; + + pc = modstatus(&R, name, sizeof(name)); + n = 10+1+strlen(name)+1+strlen(estr)+1; + p->exstr = realloc(p->exstr, n); + if(p->exstr != nil) + snprint(p->exstr, n, "%lud %s %s", pc, name, estr); + } + + /* + * there may be an uncalled frame at the top of the stack + */ + f = (Frame*)R.FP; + t = FRTYPE(f); + if(R.FP < R.EX || R.FP >= R.TS) + freeframe(R.EX+OA(Stkext, reg.tos.fr), 0); + else if(R.FP+t->size < R.SP) + freeframe(R.FP+t->size, 1); + + m = R.M; + while(R.FP != fp){ + f = (Frame*)R.FP; + R.PC = f->lr; + R.FP = f->fp; + R.SP = (uchar*)f; + mr = f->mr; + if(f->t == nil) + unextend(f); + else if(f->t->np) + freeptrs(f, f->t); + if(mr != nil){ + m = mr; + destroy(R.M); + R.M = m; + R.MP = m->MP; + } + } + if(zt != nil){ + freeptrs(fp, zt); + initmem(zt, fp); + } + eadr = (uchar**)(fp+eoff); + destroy(*eadr); + *eadr = H; + if(p->exval == H) + *eadr = (uchar*)newestring(estr); /* might fail */ + else{ + D2H(p->exval)->ref++; + *eadr = p->exval; + } + if(m->compiled) + R.PC = (Inst*)((ulong)m->prog+newpc); + else + R.PC = m->prog+newpc; + memmove(&p->R, &R, sizeof(R)); + p->kill = nil; + destroy(p->exval); + p->exval = H; + return 1; +} diff --git a/emu/port/exportfs.c b/emu/port/exportfs.c new file mode 100644 index 00000000..e88c5ad3 --- /dev/null +++ b/emu/port/exportfs.c @@ -0,0 +1,1221 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "kernel.h" + +typedef struct Fid Fid; +typedef struct Export Export; +typedef struct Exq Exq; + +enum +{ + Nfidhash = 32, + MAXFDATA = 8192, + MAXRPCDEF = IOHDRSZ+MAXFDATA, /* initial/default */ + MAXRPCMAX = IOHDRSZ+64*1024, /* most every allowed */ + MSGHDRSZ = BIT32SZ+BIT8SZ+BIT16SZ +}; + +struct Export +{ + Lock l; + Ref r; + Exq* work; + Lock fidlock; + Fid* fid[Nfidhash]; + Uqidtab uqids; + Chan* io; + Chan* root; + Pgrp* pgrp; + Egrp* egrp; + Fgrp* fgrp; + int async; + int readonly; + int uid; + int gid; + int msize; + char* user; +}; + +struct Fid +{ + Fid* next; + Fid** last; + Chan* chan; + int fid; + int ref; /* fcalls using the fid; locked by Export.Lock */ + vlong offset; /* last offset used (within directory) */ + int attached; /* fid attached or cloned but not clunked */ + Uqid* qid; /* generated qid */ +}; + +struct Exq +{ + Lock l; + int busy; /* fcall in progress */ + int finished; /* will do no more work on this request or flushes */ + Exq* next; + int shut; /* has been noted for shutdown */ + Exq* flush; /* queued flush requests */ + Exq* flusht; /* tail of flush queue */ + Export* export; + Proc* slave; + Fcall in, out; + uchar* buf; + int bsize; +}; + +struct +{ + Lock l; + QLock qwait; + Rendez rwait; + Exq *head; /* work waiting for a slave */ + Exq *tail; +}exq; + +static void exshutdown(Export*); +static int exflushed(Export*, Exq*); +static void exslave(void*); +static void exfree(Export*); +static void exfreeq(Exq*); +static void exportproc(void*); +static void exreply(Exq*, char*); +static int exisroot(Export*, Chan*); + +static char* Exversion(Export*, Fcall*, Fcall*); +static char* Exauth(Export*, Fcall*, Fcall*); +static char* Exattach(Export*, Fcall*, Fcall*); +static char* Exclunk(Export*, Fcall*, Fcall*); +static char* Excreate(Export*, Fcall*, Fcall*); +static char* Exopen(Export*, Fcall*, Fcall*); +static char* Exread(Export*, Fcall*, Fcall*); +static char* Exremove(Export*, Fcall*, Fcall*); +static char* Exstat(Export*, Fcall*, Fcall*); +static char* Exwalk(Export*, Fcall*, Fcall*); +static char* Exwrite(Export*, Fcall*, Fcall*); +static char* Exwstat(Export*, Fcall*, Fcall*); + +static char *(*fcalls[Tmax])(Export*, Fcall*, Fcall*); + +static char Enofid[] = "no such fid"; +static char Eseekdir[] = "can't seek on a directory"; +static char Eopen[] = "walk of open fid"; +static char Emode[] = "open/create -- unknown mode"; +static char Edupfid[] = "fid in use"; +static char Eaccess[] = "read/write -- not open in suitable mode"; +static char Ecount[] = "read/write -- count too big"; +int exdebug = 0; + +int +export(int fd, char *dir, int async) +{ + Chan *c, *dc; + Pgrp *pg; + Egrp *eg; + Export *fs; + + if(waserror()) + return -1; + c = fdtochan(up->env->fgrp, fd, ORDWR, 1, 1); + poperror(); + + if(waserror()){ + cclose(c); + return -1; + } + dc = namec(dir, Atodir, 0, 0); + poperror(); + + fs = malloc(sizeof(Export)); + if(fs == nil){ + cclose(c); + cclose(dc); + error(Enomem); + } + + fs->r.ref = 1; + pg = up->env->pgrp; + fs->pgrp = pg; + incref(&pg->r); + eg = up->env->egrp; + fs->egrp = eg; + incref(&eg->r); + fs->fgrp = newfgrp(nil); + fs->uid = up->env->uid; + fs->gid = up->env->gid; + kstrdup(&fs->user, up->env->user); + fs->root = dc; + fs->io = c; + uqidinit(&fs->uqids); + fs->msize = 0; + c->flag |= CMSG; + fs->async = async; + + if(async){ + if(waserror()) + return -1; + kproc("exportfs", exportproc, fs, 0); + poperror(); + }else + exportproc(fs); + + return 0; +} + +static void +exportinit(void) +{ + lock(&exq.l); + if(fcalls[Tversion] != nil) { + unlock(&exq.l); + return; + } + 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); +} + +static int +exisroot(Export *fs, Chan *c) +{ + return eqchan(fs->root, c, 1); +} + +static int +exreadn(Chan *c, void *buf, int n) +{ + int nr, t; + + if(waserror()) + return -1; + for(nr = 0; nr < n;){ + t = devtab[c->type]->read(c, (char*)buf+nr, n-nr, 0); + if(t <= 0) + break; + nr += t; + } + poperror(); + return nr; +} + +static int +exreadmsg(Chan *c, void *a, uint n) +{ + int m, len; + uchar *buf; + + buf = a; + m = exreadn(c, buf, BIT32SZ); + if(m < BIT32SZ){ + if(m < 0) + return -1; + return 0; + } + len = GBIT32(buf); + if(len <= BIT32SZ || len > n){ + kwerrstr("bad length in Styx message header"); + return -1; + } + len -= BIT32SZ; + m = exreadn(c, buf+BIT32SZ, len); + if(m < len){ + if(m < 0) + return -1; + return 0; + } + return BIT32SZ+m; +} + +static void +exportproc(void *a) +{ + Exq *q; + int async, msize; + int n, type; + Export *fs = a; + + exportinit(); + + for(;;){ + + msize = fs->msize; + if(msize == 0) + msize = MAXRPCDEF; + for(n=0;; n++){ /* we don't use smalloc, to avoid memset */ + q = mallocz(sizeof(*q)+msize, 0); + if(q != nil || n > 6000) + break; + if(n%600 == 0) + print("exportproc %ld: waiting for memory (%d) for request\n", up->pid, msize); + osenter(); + osmillisleep(100); + osleave(); + } + if(q == nil){ + kwerrstr("out of memory: read request"); + n = -1; + break; + } + memset(q, 0, sizeof(*q)); + q->buf = (uchar*)q + sizeof(*q); + q->bsize = msize; + + n = exreadmsg(fs->io, q->buf, msize); /* TO DO: avoid copy */ + if(n <= 0) + break; + if(convM2S(q->buf, n, &q->in) != n){ + kwerrstr("bad T-message"); + n = -1; + break; + } + type = q->in.type; + if(type < Tversion || type >= Tmax || type&1 || type == Terror){ + kwerrstr("invalid T-message type %d", type); + n = -1; + break; + } + + if(exdebug) + print("export %ld <- %F\n", up->pid, &q->in); + + q->out.type = type+1; + q->out.tag = q->in.tag; + + q->export = fs; + incref(&fs->r); + + if(fs->readonly){ + switch(type){ + case Topen: + if((q->in.mode & (ORCLOSE|OTRUNC|3)) == OREAD) + break; + /* FALL THROUGH */ + case Tcreate: + case Twrite: + case Tremove: + case Twstat: + q->out.type = Rerror; + q->out.ename = "file system read only"; + exreply(q, "exportproc"); + exfreeq(q); + continue; + } + } + + if(q->in.type == Tflush){ + if(exflushed(fs, q)){ + /* not yet started or not found (flush arrived after reply); we reply */ + if(exdebug) + print("export: flush %d\n", q->in.oldtag); + exreply(q, "exportproc"); + exfreeq(q); + } + continue; + } + + 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.head == nil) + kproc("exslave", exslave, nil, 0); + Wakeup(&exq.rwait); + } + + if(exdebug){ + if(n < 0) + print("exportproc %ld shut down: %s\n", up->pid, up->env->errstr); + else + print("exportproc %ld shut down\n", up->pid); + } + + free(q); + exshutdown(fs); + async = fs->async; + exfree(fs); + + if(async) + pexit("mount shut down", 0); +} + +static int +exflushed(Export *fs, Exq *fq) +{ + Exq *q, **last; + ulong pid; + + /* not yet started? */ + lock(&exq.l); + for(last = &exq.head; (q = *last) != nil; last = &q->next) + if(q->export == fs && q->in.tag == fq->in.oldtag){ + *last = q->next; + unlock(&exq.l); + /* not yet started: discard, and Rflush */ + exfreeq(q); + return 1; + } + unlock(&exq.l); + + /* tricky case: in progress */ + lock(&fs->l); + for(q = fs->work; q != nil; q = q->next) + if(q->in.tag == fq->in.oldtag){ + pid = 0; + lock(&q->l); + if(q->finished){ + /* slave replied and emptied its flush queue; we can Rflush now */ + unlock(&q->l); + return 1; + } + /* append to slave's flush queue */ + fq->next = nil; + if(q->flush != nil) + q->flusht->next = fq; + else + q->flush = fq; + q->flusht = fq; + if(q->busy){ + pid = q->slave->pid; + swiproc(q->slave, 0); + } + unlock(&q->l); + unlock(&fs->l); + if(exdebug && pid) + print("export: swiproc %ld to flush %d\n", pid, fq->in.oldtag); + return 0; + } + unlock(&fs->l); + + /* not found */ + return 1; +} + +static void +exfreeq(Exq *q) +{ + Exq *fq; + + while((fq = q->flush) != nil){ + q->flush = fq->next; + exfree(fq->export); + free(fq); + } + exfree(q->export); + free(q); +} + +static void +exshutdown(Export *fs) +{ + Exq *q, **last; + + /* work not started */ + lock(&exq.l); + for(last = &exq.head; (q = *last) != nil;) + if(q->export == fs){ + *last = q->next; + exfreeq(q); + }else + last = &q->next; + unlock(&exq.l); + + /* tell slaves to abandon work in progress */ + lock(&fs->l); + while((q = fs->work) != nil){ + fs->work = q->next; + lock(&q->l); + q->shut = 1; + swiproc(q->slave, 0); /* whether busy or not */ + unlock(&q->l); + } + unlock(&fs->l); +} + +static void +exfreefids(Export *fs) +{ + Fid *f, *n; + int i; + + for(i = 0; i < Nfidhash; i++){ + for(f = fs->fid[i]; f != nil; f = n){ + n = f->next; + f->attached = 0; + if(f->ref == 0) { + if(f->chan != nil) + cclose(f->chan); + freeuqid(&fs->uqids, f->qid); + free(f); + } else + print("exfreefids: busy fid\n"); + } + } +} + +static void +exfree(Export *fs) +{ + if(exdebug) + print("export p/s %ld free %p ref %ld\n", up->pid, fs, fs->r.ref); + if(decref(&fs->r) != 0) + return; + closepgrp(fs->pgrp); + closeegrp(fs->egrp); + closefgrp(fs->fgrp); + cclose(fs->root); + cclose(fs->io); + exfreefids(fs); + free(fs->user); + free(fs); +} + +static int +exwork(void *a) +{ + USED(a); + return exq.head != nil; +} + +static void +exslave(void *a) +{ + Export *fs; + Exq *q, *t, *fq, **last; + char *err; + + USED(a); + + for(;;){ + qlock(&exq.qwait); + if(waserror()){ + qunlock(&exq.qwait); + continue; + } + Sleep(&exq.rwait, exwork, nil); + poperror(); + + lock(&exq.l); + q = exq.head; + if(q == nil) { + unlock(&exq.l); + qunlock(&exq.qwait); + continue; + } + exq.head = q->next; + + qunlock(&exq.qwait); + + /* + * put the job on the work queue before it's + * visible as off of the head queue, so it's always + * findable for flushes and shutdown + */ + notkilled(); + q->slave = up; + q->busy = 1; /* fcall in progress: interruptible */ + fs = q->export; + lock(&fs->l); + q->next = fs->work; + fs->work = q; + unlock(&fs->l); + unlock(&exq.l); + + up->env->pgrp = q->export->pgrp; + up->env->egrp = q->export->egrp; + up->env->fgrp = q->export->fgrp; + up->env->uid = q->export->uid; + up->env->gid = q->export->gid; + kstrdup(&up->env->user, q->export->user); + + if(exdebug > 1) + print("exslave %ld dispatch %F\n", up->pid, &q->in); + + if(waserror()){ + print("exslave %ld err %s\n", up->pid, up->env->errstr); /* shouldn't happen */ + err = up->env->errstr; + }else{ + if(q->in.type >= Tmax || !fcalls[q->in.type]){ + snprint(up->genbuf, sizeof(up->genbuf), "unknown message: %F", &q->in); + err = up->genbuf; + }else{ + switch(q->in.type){ + case Tread: + q->out.data = (char*)q->buf + IOHDRSZ; + break; + case Tstat: + q->out.stat = q->buf + MSGHDRSZ + BIT16SZ; /* leaves it just where we want it */ + q->out.nstat = q->bsize-(MSGHDRSZ+BIT16SZ); + break; + } + err = (*fcalls[q->in.type])(fs, &q->in, &q->out); + } + poperror(); + } + + /* + * if the fcall completed without error we must reply, + * even if a flush is pending (because the underlying server + * might have changed state), unless the export has shut down completely. + * must also reply to each flush in order, and only after the original reply (if sent). + */ + lock(&q->l); + notkilled(); + q->busy = 0; /* operation complete */ + if(!q->shut){ + if(q->flush == nil || err == nil){ + unlock(&q->l); + q->out.type = q->in.type+1; + q->out.tag = q->in.tag; + if(err){ + q->out.type = Rerror; + q->out.ename = err; + } + exreply(q, "exslave"); + lock(&q->l); + } + while((fq = q->flush) != nil && !q->shut){ + q->flush = fq->next; + unlock(&q->l); + exreply(fq, "exslave"); + exfreeq(fq); + lock(&q->l); + } + } + q->finished = 1; /* promise not to send any more */ + unlock(&q->l); + + lock(&fs->l); + for(last = &fs->work; (t = *last) != nil; last = &t->next) + if(t == q){ + *last = q->next; + break; + } + unlock(&fs->l); + + notkilled(); + exfreeq(q); + } + print("exslave %ld shut down", up->pid); /* not reached */ + pexit("exslave shut down", 0); +} + +static void +exreply(Exq *q, char *who) +{ + Export *fs; + Fcall *r; + int n; + + fs = q->export; + r = &q->out; + + n = convS2M(r, q->buf, q->bsize); + if(n == 0){ + r->type = Rerror; + if(fs->msize == 0) + r->ename = "Tversion not seen"; + else + r->ename = "failed to convert R-message"; + n = convS2M(r, q->buf, q->bsize); + } + + if(exdebug) + print("%s %ld -> %F\n", who, up->pid, r); + + if(!waserror()){ + devtab[fs->io->type]->write(fs->io, q->buf, n, 0); + poperror(); + } +} + +static int +exiounit(Export *fs, Chan *c) +{ + int iounit; + + iounit = fs->msize-IOHDRSZ; + if(c->iounit != 0 && c->iounit < fs->msize) + iounit = c->iounit; + return iounit; +} + +static Fid* +Exmkfid(Export *fs, ulong fid) +{ + ulong h; + Fid *f, *nf; + + nf = malloc(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; + nf->qid = nil; + unlock(&fs->fidlock); + return nf; +} + +static Fid* +Exgetfid(Export *fs, ulong 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; +} + +static void +Exputfid(Export *fs, Fid *f) +{ + Chan *c; + + lock(&fs->fidlock); + f->ref--; + if(f->ref == 0 && f->attached == 0){ + c = f->chan; + f->chan = nil; + *f->last = f->next; + if(f->next != nil) + f->next->last = f->last; + unlock(&fs->fidlock); + freeuqid(&fs->uqids, f->qid); + free(f); + if(c != nil) + cclose(c); + return; + } + unlock(&fs->fidlock); +} + +static Chan* +exmount(Chan *c, Mhead **mp, int doname) +{ + struct {Chan *nc;} nc; + Cname *oname; + + nc.nc = nil; + if((c->flag & COPEN) == 0 && findmount(&nc.nc, mp, c->type, c->dev, c->qid)){ + if(waserror()){ + cclose(nc.nc); + nexterror(); + } + nc.nc = cunique(nc.nc); + poperror(); + if(doname){ + oname = c->name; + incref(&oname->r); + cnameclose(nc.nc->name); + nc.nc->name = oname; + } + return nc.nc; + } + incref(&c->r); + return c; +} + +static char* +Exversion(Export *fs, Fcall *t, Fcall *r) +{ + char *p; + static char version[] = VERSION9P; + int iounit; + + r->msize = t->msize; + if(r->msize > MAXRPCMAX) + r->msize = MAXRPCMAX; + iounit = fs->io->iounit; + if(iounit != 0 && iounit > 64 && iounit < r->msize) + r->msize = iounit; + if(r->msize < 64) + return "message size too small"; + if(0) + print("msgsize=%d\n", r->msize); + if((p = strchr(t->version, '.')) != nil) + *p = 0; + if(strncmp(t->version, "9P", 2) ==0 && strcmp(version, t->version) <= 0){ + r->version = version; + fs->msize = r->msize; + }else + r->version = "unknown"; + return nil; +} + +static char* +Exauth(Export *fs, Fcall *t, Fcall *r) +{ + USED(fs); + USED(t); + USED(r); + return "authentication not required"; +} + +static char* +Exattach(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + + f = Exmkfid(fs, t->fid); + if(f == nil) + return Edupfid; + if(waserror()){ + f->attached = 0; + Exputfid(fs, f); + return up->env->errstr; + } + f->chan = cclone(fs->root); + f->qid = uqidalloc(&fs->uqids, f->chan); + poperror(); + r->qid = mkuqid(f->chan, f->qid); + Exputfid(fs, f); + return nil; +} + +static char* +Exclunk(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + + USED(r); + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + f->attached = 0; + Exputfid(fs, f); + return nil; +} + +static int +safewalk(Chan **cp, char **names, int nnames, int nomount, int *nerror) +{ + int r; + + /* walk can raise error */ + if(waserror()) + return -1; + r = walk(cp, names, nnames, nomount, nerror); + poperror(); + return r; +} + +static char* +Exwalk(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f, *nf; + Chan *c; + char *name; + Uqid *qid; + int i; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(f->chan->flag & COPEN){ + Exputfid(fs, f); + return Eopen; + } + if(waserror()) + return up->env->errstr; + c = cclone(f->chan); + poperror(); + qid = f->qid; + incref(&qid->r); + r->nwqid = 0; + if(t->nwname > 0){ + for(i=0; i<t->nwname; i++){ + name = t->wname[i]; + if(!exisroot(fs, c) || *name != '\0' && strcmp(name, "..") != 0){ + if(safewalk(&c, &name, 1, 0, nil) < 0){ + /* leave the original state on error */ + cclose(c); + freeuqid(&fs->uqids, qid); + Exputfid(fs, f); + if(i == 0) + return up->env->errstr; + return nil; + } + freeuqid(&fs->uqids, qid); + qid = uqidalloc(&fs->uqids, c); + } + r->wqid[r->nwqid++] = mkuqid(c, qid); + } + } + + if(t->newfid != t->fid){ + nf = Exmkfid(fs, t->newfid); + if(nf == nil){ + cclose(c); + freeuqid(&fs->uqids, qid); + Exputfid(fs, f); + return Edupfid; + } + nf->chan = c; + nf->qid = qid; + Exputfid(fs, nf); + }else{ + cclose(f->chan); + f->chan = c; + freeuqid(&fs->uqids, f->qid); + f->qid = qid; + } + Exputfid(fs, f); + return nil; +} + +static char* +Exopen(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + Uqid *qid; + Mhead *m; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(f->chan->flag & COPEN){ + Exputfid(fs, f); + return Emode; + } + m = nil; + c = exmount(f->chan, &m, 1); + if(waserror()){ + cclose(c); + Exputfid(fs, f); + return up->env->errstr; + } + + /* 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); + + c = devtab[c->type]->open(c, t->mode); + if(t->mode & ORCLOSE) + c->flag |= CRCLOSE; + + qid = uqidalloc(&fs->uqids, c); + poperror(); + freeuqid(&fs->uqids, f->qid); + cclose(f->chan); + f->chan = c; + f->qid = qid; + f->offset = 0; + r->qid = mkuqid(c, f->qid); + r->iounit = exiounit(fs, c); + Exputfid(fs, f); + return nil; +} + +static char* +Excreate(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + volatile struct {Chan *c;} c, dc; + Cname *oname; + Uqid *qid; + Mhead *m; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(f->chan->flag & COPEN){ + Exputfid(fs, f); + return Emode; + } + if(waserror()){ + Exputfid(fs, f); + return up->env->errstr; + } + validname(t->name, 0); + if(t->name[0] == '.' && (t->name[1] == '\0' || t->name[1] == '.' && t->name[2] == '\0')) + error(Efilename); /* underlying server should check, but stop it here */ + + m = nil; + c.c = exmount(f->chan, &m, 1); + if(waserror()){ + cclose(c.c); + if(m != nil) + putmhead(m); + nexterror(); + } + if(m != nil){ + oname = c.c->name; + incref(&oname->r); + if(waserror()){ + cnameclose(oname); + nexterror(); + } + dc.c = createdir(c.c, m); + if(waserror()){ + cclose(dc.c); + nexterror(); + } + c.c = cunique(dc.c); + poperror(); + cnameclose(c.c->name); + poperror(); + c.c->name = oname; + } + devtab[c.c->type]->create(c.c, t->name, t->mode, t->perm); + c.c->name = addelem(c.c->name, t->name); + if(t->mode & ORCLOSE) + c.c->flag |= CRCLOSE; + qid = uqidalloc(&fs->uqids, c.c); + poperror(); + if(m != nil) + putmhead(m); + + poperror(); + cclose(f->chan); + f->chan = c.c; + freeuqid(&fs->uqids, f->qid); + f->qid = qid; + r->qid = mkuqid(c.c, f->qid); + r->iounit = exiounit(fs, c.c); + Exputfid(fs, f); + return nil; +} + +static char* +Exread(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + Lock *cl; + long off; + int dir, n, seek; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + + if(waserror()) { + Exputfid(fs, f); + return up->env->errstr; + } + c = f->chan; + if((c->flag & COPEN) == 0) + error(Emode); + if(c->mode != OREAD && c->mode != ORDWR) + error(Eaccess); + if(t->count < 0 || t->count > fs->msize-IOHDRSZ) + error(Ecount); + if(t->offset < 0) + error(Enegoff); + dir = c->qid.type & QTDIR; + if(dir && t->offset != f->offset){ + if(t->offset != 0) + error(Eseekdir); + f->offset = 0; + c->uri = 0; + c->dri = 0; + } + + for(;;){ + n = t->count; + seek = 0; + off = t->offset; + if(dir && f->offset != off){ + off = f->offset; + n = t->offset - off; + if(n > MAXFDATA) + n = MAXFDATA; + seek = 1; + } + if(dir && c->umh != nil){ + if(0) + print("union read %d uri %d dri %d\n", seek, c->uri, c->dri); + n = unionread(c, r->data, n); + } + else{ + cl = &c->l; + lock(cl); + c->offset = off; + unlock(cl); + n = devtab[c->type]->read(c, r->data, n, off); + lock(cl); + c->offset += n; + unlock(cl); + } + f->offset = off + n; + if(n == 0 || !seek) + break; + } + r->count = n; + + poperror(); + Exputfid(fs, f); + return nil; +} + +static char* +Exwrite(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->env->errstr; + } + c = f->chan; + if((c->flag & COPEN) == 0) + error(Emode); + if(c->mode != OWRITE && c->mode != ORDWR) + error(Eaccess); + if(c->qid.type & QTDIR) + error(Eisdir); + if(t->count < 0 || t->count > fs->msize-IOHDRSZ) + error(Ecount); + if(t->offset < 0) + error(Enegoff); + r->count = devtab[c->type]->write(c, t->data, t->count, t->offset); + poperror(); + Exputfid(fs, f); + return nil; +} + +static char* +Exstat(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + int n; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + c = exmount(f->chan, nil, 1); + if(waserror()){ + cclose(c); + Exputfid(fs, f); + return up->env->errstr; + } + n = devtab[c->type]->stat(c, r->stat, r->nstat); + if(n <= BIT16SZ) + error(Eshortstat); + r->nstat = n; + poperror(); + /* TO DO: need to change qid */ + cclose(c); + Exputfid(fs, f); + return nil; +} + +static char* +Exwstat(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + + USED(r); + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->env->errstr; + } + validstat(t->stat, t->nstat); /* check name */ + + c = exmount(f->chan, nil, 0); + if(waserror()){ + cclose(c); + nexterror(); + } + devtab[c->type]->wstat(c, t->stat, t->nstat); + poperror(); + + cclose(c); + poperror(); + Exputfid(fs, f); + return nil; +} + +static char* +Exremove(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + + USED(r); + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(waserror()){ + f->attached = 0; + Exputfid(fs, f); + return up->env->errstr; + } + c = exmount(f->chan, nil, 0); + if(waserror()){ + c->type = 0; /* see below */ + cclose(c); + nexterror(); + } + devtab[c->type]->remove(c); + poperror(); + poperror(); + + /* + * chan is already clunked by remove. + * however, we need to recover the chan, + * and follow sysremove's lead in making it point to root. + */ + c->type = 0; + cclose(c); + + f->attached = 0; + Exputfid(fs, f); + return nil; +} diff --git a/emu/port/exptab.c b/emu/port/exptab.c new file mode 100644 index 00000000..782ef21d --- /dev/null +++ b/emu/port/exptab.c @@ -0,0 +1,6 @@ +#include "dat.h" +#include <dynld.h> + +/* dummy export table */ + +Dynsym _exporttab[] = { 0, 0, nil }; diff --git a/emu/port/file.c b/emu/port/file.c new file mode 100644 index 00000000..91000ebb --- /dev/null +++ b/emu/port/file.c @@ -0,0 +1,16 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +int +openmode(ulong o) +{ + if(o >= (OTRUNC|OCEXEC|ORCLOSE|OEXEC)) + error(Ebadarg); + o &= ~(OTRUNC|OCEXEC|ORCLOSE); + if(o > OEXEC) + error(Ebadarg); + if(o == OEXEC) + return OREAD; + return o; +} diff --git a/emu/port/fns.h b/emu/port/fns.h new file mode 100644 index 00000000..393a5c35 --- /dev/null +++ b/emu/port/fns.h @@ -0,0 +1,244 @@ +ulong FPcontrol(ulong,ulong); +ulong FPstatus(ulong,ulong); +void FPsave(void*); +void FPrestore(void*); +void Sleep(Rendez*, int (*)(void*), void*); +int Wakeup(Rendez*); +void FPinit(void); +void addprog(Proc*); +Block* adjustblock(Block*, int); +Block* allocb(int); +Block* bl2mem(uchar*, Block*, int); +int blocklen(Block*); +char* c2name(Chan*); +int canlock(Lock*); +int canqlock(QLock*); +void cclose(Chan*); +void chandevinit(void); +void chanfree(Chan*); +Dir* chandirstat(Chan*); +void cinit(void); +char* clipread(void); +int clipwrite(char*); +void copen(Chan*); +void cmderror(Cmdbuf*, char*); +int cread(Chan*, uchar*, int, vlong); +void cwrite(Chan*, uchar*, int, vlong); +Chan* cunique(Chan*); +void cupdate(Chan*, uchar*, int, vlong); +char* cleanname(char*); +Chan* cclone(Chan*); +void closeegrp(Egrp*); +void closefgrp(Fgrp*); +void closepgrp(Pgrp*); +void closesigs(Skeyset*); +int cmount(Chan*, Chan*, int, char*); +Chan* createdir(Chan*, Mhead*); +void cunmount(Chan*, Chan*); +int decref(Ref*); +long devbwrite(Chan*, Block*, ulong); +void devcreate(Chan*, char*, int, ulong); +void devdir(Chan*, Qid, char*, long, char*, long, Dir*); +long devdirread(Chan*, char*, long, Dirtab*, int, Devgen*); +void devinit(void); +int devno(int, int); +Dev* devbyname(char*); +void devpermcheck(char*, ulong, int); +void devremove(Chan*); +int devstat(Chan*, uchar*, int, Dirtab*, int, Devgen*); +int devwstat(Chan*, uchar*, int); +Chan* devattach(int, char*); +Block* devbread(Chan*, long, ulong); +Chan* devclone(Chan*); +Devgen devgen; +Chan* devopen(Chan*, int, Dirtab*, int, Devgen*); +Walkqid* devwalk(Chan*, Chan*, char**, int, Dirtab*, int, Devgen*); +void disfault(void*, char*); +void disinit(void*); +int domount(Chan**, Mhead**); +void drawqlock(void); +void drawqunlock(void); +Fgrp* dupfgrp(Fgrp*); +void egrpcpy(Egrp*, Egrp*); +int emptystr(char*); +void emuinit(void*); +int eqchan(Chan*, Chan*, int); +int eqqid(Qid, Qid); +void error(char*); +void errorf(char*, ...); +#pragma varargck argpos errorf 1 +void excinit(void); +void exhausted(char*); +int export(int, char*, int); +Chan* fdtochan(Fgrp*, int, int, int, int); +int findmount(Chan**, Mhead**, int, int, Qid); +void freeb(Block*); +void freeblist(Block*); +void freeskey(Signerkey*); +ulong getcallerpc(void*); +ulong getFPcontrol(void); +ulong getFPstatus(void); +void gkbdputc(Queue*, int); +int incref(Ref*); +int iprint(char*, ...); +void isdir(Chan*); +int isdotdot(char*); +int iseve(void); +int kannounce(char*, char*); +int kdial(char*, char*, char*, int*); +int kproc(char*, void (*)(void*), void*, int); +int kfgrpclose(Fgrp*, int); +void ksetenv(char*, char*, int); +void kstrcpy(char*, char*, int); +void kstrdup(char**, char*); +long latin1(uchar*, int); +void libinit(char*); +void links(void); +void lock(Lock*); +Cmdtab* lookupcmd(Cmdbuf*, Cmdtab*, int); +Block* mem2bl(uchar*, int); +int memusehigh(void); +int memlow(void); +void mkqid(Qid*, vlong, ulong, int); +Qid mkuqid(Chan*, Uqid*); +Chan* mntauth(Chan*, char*); +long mntversion(Chan*, char*, int, int); +void mountfree(Mount*); +void mousetrack(int, int, int, int); +void muxclose(Mnt*); +Chan* namec(char*, int, int, ulong); +Chan* newchan(void); +Cname* newcname(char*); +Egrp* newegrp(void); +Fgrp* newfgrp(Fgrp*); +Mount* newmount(Mhead*, Chan*, int, char*); +Pgrp* newpgrp(void); +Proc* newproc(void); +void nexterror(void); +void notkilled(void); +int openmode(ulong); +void osblock(void); +void* oscmd(char**, int, char*, int*, int*); +int oscmdwait(void*, char*, int); +int oscmdkill(void*); +void oscmdfree(void*); +void oserror(void); +void oserrstr(char*, uint); +void oslongjmp(void*, osjmpbuf, int); +long osmillisec(void); +int osmillisleep(ulong); +void osready(Proc*); +int limbosleep(ulong); +vlong osusectime(void); +Block* packblock(Block*); +Block* padblock(Block*, int); +void panic(char*, ...); +Cmdbuf* parsecmd(char*, int); +void pexit(char*, int); +void pgrpcpy(Pgrp*, Pgrp*); +int progfdprint(Chan*, int, int, char*, int); +void putenvq(char*, char*, int); +void putenvqv(char*, char**, int, int); +void putmhead(Mhead*); +Block* pullupblock(Block*, int); +Block* pullupqueue(Queue*, int); +void qaddlist(Queue*, Block*); +Block* qbread(Queue*, int); +long qbwrite(Queue*, Block*); +int qcanread(Queue*); +void qclose(Queue*); +int qisclosed(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 qiwrite(Queue*, void*, int); +int qlen(Queue*); +void qlock(QLock*); +void qnoblock(Queue*, int); +Queue* qopen(int, int, void (*)(void*), void*); +int qpass(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); +int qstate(Queue*); +void qunlock(QLock*); +int qwindow(Queue*); +int qwrite(Queue*, void*, int); +ulong randomread(void *xp, ulong n); +void randominit(void); +int readkbd(void); +int readnum(ulong, char*, ulong, ulong, int); +int readnum_vlong(ulong, char*, ulong, vlong, int); +int readstr(ulong, char*, ulong, char*); +#define seconds() (osusectime()/1000000) +void seterror(char*, ...); +void setid(char*, int); +void setpointer(int, int); +char* skipslash(char*); +void srvrtinit(void); +void swiproc(Proc*, int); +long unionread(Chan*, void*, long); +void unlock(Lock*); +Uqid* uqidalloc(Uqidtab*, Chan*); +void uqidinit(Uqidtab*); +void freeuqid(Uqidtab*, Uqid*); +void validname(char*, int); +void validstat(uchar*, int); +void validwstatname(char*); +void vmachine(void*); +int walk(Chan**, char**, int, int, int*); +void cleanexit(int); +void oshostintr(Proc*); +void osenter(void); +void osleave(void); +void oslopri(void); +void ospause(void); +void osyield(void); +void osreboot(char*, char**); +ulong poolmaxsize(void); +Pool* poolmk(char*, int, int, int); +void hnputv(void*, vlong); +void hnputl(void*, ulong); +void hnputs(void*, ushort); +vlong nhgetv(void*); +ulong nhgetl(void*); +ushort nhgets(void*); +void* smalloc(size_t); +void* kmalloc(size_t); + +/* Namespace Emulation */ +int kbind(char*, char*, int); +int kclose(int); +int kcreate(char*, int, ulong); +int kdup(int, int); +int kfstat(int, uchar*, int); +int kfwstat(int, uchar*, int); +int kmount(int, int, char*, int, char*); +int kunmount(char*, char*); +int kopen(char*, int); +long kread(int, void*, long); +int kremove(char*); +vlong kseek(int, vlong, int); +int kstat(char*, uchar*, int); +long kwrite(int, void*, long); +int kwstat(char*, uchar*, int); +Dir* kdirstat(char*); +Dir* kdirfstat(int); +int kdirwstat(char*, Dir*); +int kdirfwstat(int, Dir*); +long kdirread(int, Dir**); +int klisten(char*, char*); + +Cname* addelem(Cname*, char*); +void cleancname(Cname*); +void cnameclose(Cname*); + +#pragma varargck argpos iprint 1 diff --git a/emu/port/inferno.c b/emu/port/inferno.c new file mode 100644 index 00000000..736644be --- /dev/null +++ b/emu/port/inferno.c @@ -0,0 +1,1029 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "interp.h" +#include "isa.h" +#include "runt.h" +#include "kernel.h" + +/* + * here because Sys_FileIO is not public + */ +extern int srvf2c(char*, char*, Sys_FileIO*); + +/* + * System types connected to gc + */ +uchar FDmap[] = Sys_FD_map; +uchar FileIOmap[] = Sys_FileIO_map; +void freeFD(Heap*, int); +void freeFileIO(Heap*, int); +Type* TFD; +Type* TFileIO; + +static uchar rmap[] = Sys_FileIO_read_map; +static uchar wmap[] = Sys_FileIO_write_map; +static Type* FioTread; +static Type* FioTwrite; +static uchar dmap[] = Sys_Dir_map; +static Type* Tdir; + +typedef struct FD FD; +struct FD +{ + Sys_FD fd; + Fgrp* grp; +}; + +void +sysinit(void) +{ + TFD = dtype(freeFD, sizeof(FD), FDmap, sizeof(FDmap)); + TFileIO = dtype(freeFileIO, Sys_FileIO_size, FileIOmap, sizeof(FileIOmap)); + + /* Support for devsrv.c */ + FioTread = dtype(freeheap, Sys_FileIO_read_size, rmap, sizeof(rmap)); + FioTwrite = dtype(freeheap, Sys_FileIO_write_size, wmap, sizeof(wmap)); + + /* Support for dirread */ + Tdir = dtype(freeheap, Sys_Dir_size, dmap, sizeof(dmap)); +} + +void +freeFD(Heap *h, int swept) +{ + FD *handle; + + USED(swept); + + handle = H2D(FD*, h); + + release(); + if(handle->fd.fd >= 0) + kfgrpclose(handle->grp, handle->fd.fd); + closefgrp(handle->grp); + acquire(); +} + +void +freeFileIO(Heap *h, int swept) +{ + Sys_FileIO *fio; + + if(swept) + return; + + fio = H2D(Sys_FileIO*, h); + destroy(fio->read); + destroy(fio->write); +} + +Sys_FD* +mkfd(int fd) +{ + Heap *h; + Fgrp *fg; + FD *handle; + + h = heap(TFD); + handle = H2D(FD*, h); + handle->fd.fd = fd; + fg = up->env->fgrp; + handle->grp = fg; + incref(&fg->r); + return (Sys_FD*)handle; +} +#define fdchk(x) ((x) == (Sys_FD*)H ? -1 : (x)->fd) + +void +seterror(char *err, ...) +{ + char *estr; + va_list arg; + + estr = up->env->errstr; + va_start(arg, err); + vseprint(estr, estr+ERRMAX, err, arg); + va_end(arg); +} + +char* +syserr(char *s, char *es, Prog *p) +{ + Osenv *o; + + o = p->osenv; + kstrcpy(s, o->errstr, es - s); + return s + strlen(s); +} + +void +Sys_millisec(void *fp) +{ + F_Sys_millisec *f; + + f = fp; + *f->ret = osmillisec(); +} + +void +Sys_open(void *fp) +{ + int fd; + F_Sys_open *f; + + f = fp; + destroy(*f->ret); + *f->ret = H; + release(); + fd = kopen(string2c(f->s), f->mode); + acquire(); + if(fd == -1) + return; + + *f->ret = mkfd(fd); +} + +void +Sys_pipe(void *fp) +{ + Array *a; + int fd[2]; + Sys_FD **sfd; + F_Sys_pipe *f; + + f = fp; + *f->ret = -1; + + a = f->fds; + if(a->len < 2) + return; + if(kpipe(fd) < 0) + return; + + sfd = (Sys_FD**)a->data; + destroy(sfd[0]); + destroy(sfd[1]); + sfd[0] = H; + sfd[1] = H; + sfd[0] = mkfd(fd[0]); + sfd[1] = mkfd(fd[1]); + *f->ret = 0; +} + +void +Sys_fildes(void *fp) +{ + F_Sys_fildes *f; + int fd; + + f = fp; + destroy(*f->ret); + *f->ret = H; + release(); + fd = kdup(f->fd, -1); + acquire(); + if(fd == -1) + return; + *f->ret = mkfd(fd); +} + +void +Sys_dup(void *fp) +{ + F_Sys_dup *f; + + f = fp; + release(); + *f->ret = kdup(f->old, f->new); + acquire(); +} + +void +Sys_create(void *fp) +{ + int fd; + F_Sys_create *f; + + f = fp; + destroy(*f->ret); + *f->ret = H; + release(); + fd = kcreate(string2c(f->s), f->mode, f->perm); + acquire(); + if(fd == -1) + return; + + *f->ret = mkfd(fd); +} + +void +Sys_remove(void *fp) +{ + F_Sys_remove *f; + + f = fp; + release(); + *f->ret = kremove(string2c(f->s)); + acquire(); +} + +void +Sys_seek(void *fp) +{ + F_Sys_seek *f; + + f = fp; + release(); + *f->ret = kseek(fdchk(f->fd), f->off, f->start); + acquire(); +} + +void +Sys_unmount(void *fp) +{ + F_Sys_unmount *f; + + f = fp; + release(); + *f->ret = kunmount(string2c(f->s1), string2c(f->s2)); + acquire(); +} + +void +Sys_read(void *fp) +{ + int n; + F_Sys_read *f; + + f = fp; + n = f->n; + if(f->buf == (Array*)H || n < 0) { + *f->ret = 0; + return; + } + if(n > f->buf->len) + n = f->buf->len; + + release(); + *f->ret = kread(fdchk(f->fd), f->buf->data, n); + acquire(); +} + +void +Sys_pread(void *fp) +{ + int n; + F_Sys_pread *f; + + f = fp; + n = f->n; + if(f->buf == (Array*)H || n < 0) { + *f->ret = 0; + return; + } + if(n > f->buf->len) + n = f->buf->len; + + release(); + *f->ret = kpread(fdchk(f->fd), f->buf->data, n, f->off); + acquire(); +} + +void +Sys_chdir(void *fp) +{ + F_Sys_chdir *f; + + f = fp; + release(); + *f->ret = kchdir(string2c(f->path)); + acquire(); +} + +void +Sys_write(void *fp) +{ + int n; + F_Sys_write *f; + + f = fp; + n = f->n; + if(f->buf == (Array*)H || n < 0) { + *f->ret = 0; + return; + } + if(n > f->buf->len) + n = f->buf->len; + + release(); + *f->ret = kwrite(fdchk(f->fd), f->buf->data, n); + acquire(); +} + +void +Sys_pwrite(void *fp) +{ + int n; + F_Sys_pwrite *f; + + f = fp; + n = f->n; + if(f->buf == (Array*)H || n < 0) { + *f->ret = 0; + return; + } + if(n > f->buf->len) + n = f->buf->len; + + release(); + *f->ret = kpwrite(fdchk(f->fd), f->buf->data, n, f->off); + acquire(); +} + +static void +unpackdir(Dir *d, Sys_Dir *sd) +{ + retstr(d->name, &sd->name); + retstr(d->uid, &sd->uid); + retstr(d->gid, &sd->gid); + retstr(d->muid, &sd->muid); + sd->qid.path = d->qid.path; + sd->qid.vers = d->qid.vers; + sd->qid.qtype = d->qid.type; + sd->mode = d->mode; + sd->atime = d->atime; + sd->mtime = d->mtime; + sd->length = d->length; + sd->dtype = d->type; + sd->dev = d->dev; +} + +static Dir* +packdir(Sys_Dir *sd) +{ + char *nm[4], *p; + int i, n; + Dir *d; + + nm[0] = string2c(sd->name); + nm[1] = string2c(sd->uid); + nm[2] = string2c(sd->gid); + nm[3] = string2c(sd->muid); + n = 0; + for(i=0; i<4; i++) + n += strlen(nm[i])+1; + d = smalloc(sizeof(*d)+n); + p = (char*)d+sizeof(*d); + for(i=0; i<4; i++){ + n = strlen(nm[i])+1; + memmove(p, nm[i], n); + nm[i] = p; + p += n; + } + d->name = nm[0]; + d->uid = nm[1]; + d->gid = nm[2]; + d->muid = nm[3]; + d->qid.path = sd->qid.path; + d->qid.vers = sd->qid.vers; + d->qid.type = sd->qid.qtype; + d->mode = sd->mode; + d->atime = sd->atime; + d->mtime = sd->mtime; + d->length = sd->length; + d->type = sd->dtype; + d->dev = sd->dev; + return d; +} + +void +Sys_fstat(void *fp) +{ + Dir *d; + F_Sys_fstat *f; + + f = fp; + f->ret->t0 = -1; + release(); + d = kdirfstat(fdchk(f->fd)); + acquire(); + if(d == nil) + return; + if(waserror() == 0){ + unpackdir(d, &f->ret->t1); + f->ret->t0 = 0; + poperror(); + } + free(d); +} + +void +Sys_stat(void *fp) +{ + Dir *d; + F_Sys_stat *f; + + f = fp; + f->ret->t0 = -1; + release(); + d = kdirstat(string2c(f->s)); + acquire(); + if(d == nil) + return; + if(waserror() == 0){ + unpackdir(d, &f->ret->t1); + f->ret->t0 = 0; + poperror(); + } + free(d); +} + +void +Sys_fd2path(void *fp) +{ + F_Sys_fd2path *f; + char *s; + void *r; + + f = fp; + r = *f->ret; + *f->ret = H; + destroy(r); + release(); + s = kfd2path(fdchk(f->fd)); + acquire(); + if(waserror() == 0){ + retstr(s, f->ret); + poperror(); + } + free(s); +} + +void +Sys_mount(void *fp) +{ + F_Sys_mount *f; + + f = fp; + release(); + *f->ret = kmount(fdchk(f->fd), fdchk(f->afd), string2c(f->on), f->flags, string2c(f->spec)); + acquire(); +} + +void +Sys_bind(void *fp) +{ + F_Sys_bind *f; + + f = fp; + release(); + *f->ret = kbind(string2c(f->s), string2c(f->on), f->flags); + acquire(); +} + +void +Sys_wstat(void *fp) +{ + Dir *d; + F_Sys_wstat *f; + + f = fp; + d = packdir(&f->d); + release(); + *f->ret = kdirwstat(string2c(f->s), d); + acquire(); + free(d); +} + +void +Sys_fwstat(void *fp) +{ + Dir *d; + F_Sys_fwstat *f; + + f = fp; + d = packdir(&f->d); + release(); + *f->ret = kdirfwstat(fdchk(f->fd), d); + acquire(); + free(d); +} + +void +Sys_print(void *fp) +{ + int n; + Prog *p; + Chan *c; + char buf[1024], *b = buf; + F_Sys_print *f; + f = fp; + c = up->env->fgrp->fd[1]; + if(c == nil) + return; + p = currun(); + + release(); + n = xprint(p, f, &f->vargs, f->s, buf, sizeof(buf)); + if (n >= sizeof(buf)-UTFmax-2) + n = bigxprint(p, f, &f->vargs, f->s, &b, sizeof(buf)); + *f->ret = kwrite(1, b, n); + if (b != buf) + free(b); + acquire(); +} + +void +Sys_fprint(void *fp) +{ + int n; + Prog *p; + char buf[1024], *b = buf; + F_Sys_fprint *f; + + f = fp; + p = currun(); + release(); + n = xprint(p, f, &f->vargs, f->s, buf, sizeof(buf)); + if (n >= sizeof(buf)-UTFmax-2) + n = bigxprint(p, f, &f->vargs, f->s, &b, sizeof(buf)); + *f->ret = kwrite(fdchk(f->fd), b, n); + if (b != buf) + free(b); + acquire(); +} + +void +Sys_werrstr(void *fp) +{ + F_Sys_werrstr *f; + + f = fp; + *f->ret = 0; + kstrcpy(up->env->errstr, string2c(f->s), ERRMAX); +} + +void +Sys_dial(void *fp) +{ + int cfd; + char dir[NETPATHLEN], *a, *l; + F_Sys_dial *f; + + f = fp; + a = string2c(f->addr); + l = string2c(f->local); + release(); + f->ret->t0 = kdial(a, l, dir, &cfd); + acquire(); + destroy(f->ret->t1.dfd); + f->ret->t1.dfd = H; + destroy(f->ret->t1.cfd); + f->ret->t1.cfd = H; + if(f->ret->t0 == -1) + return; + + f->ret->t1.dfd = mkfd(f->ret->t0); + f->ret->t1.cfd = mkfd(cfd); + retstr(dir, &f->ret->t1.dir); +} + +void +Sys_announce(void *fp) +{ + char dir[NETPATHLEN], *a; + F_Sys_announce *f; + + f = fp; + a = string2c(f->addr); + release(); + f->ret->t0 = kannounce(a, dir); + acquire(); + destroy(f->ret->t1.dfd); + f->ret->t1.dfd = H; + destroy(f->ret->t1.cfd); + f->ret->t1.cfd = H; + if(f->ret->t0 == -1) + return; + + f->ret->t1.cfd = mkfd(f->ret->t0); + retstr(dir, &f->ret->t1.dir); +} + +void +Sys_listen(void *fp) +{ + F_Sys_listen *f; + char dir[NETPATHLEN], *d; + + f = fp; + d = string2c(f->c.dir); + release(); + f->ret->t0 = klisten(d, dir); + acquire(); + + destroy(f->ret->t1.dfd); + f->ret->t1.dfd = H; + destroy(f->ret->t1.cfd); + f->ret->t1.cfd = H; + if(f->ret->t0 == -1) + return; + + f->ret->t1.cfd = mkfd(f->ret->t0); + retstr(dir, &f->ret->t1.dir); +} + +void +Sys_sleep(void *fp) +{ + F_Sys_sleep *f; + + f = fp; + release(); + if(f->period > 0){ + if(waserror()){ + acquire(); + error(""); + } + osenter(); + *f->ret = limbosleep(f->period); + osleave(); + poperror(); + } + acquire(); +} + +void +Sys_stream(void *fp) +{ + Prog *p; + uchar *buf; + int src, dst; + F_Sys_stream *f; + int nbytes, t, n; + + f = fp; + buf = malloc(f->bufsiz); + if(buf == nil) { + kwerrstr(Enomem); + *f->ret = -1; + return; + } + + src = fdchk(f->src); + dst = fdchk(f->dst); + + p = currun(); + + release(); + t = 0; + nbytes = 0; + while(p->kill == nil) { + n = kread(src, buf+t, f->bufsiz-t); + if(n <= 0) + break; + t += n; + if(t >= f->bufsiz) { + if(kwrite(dst, buf, t) != t) { + t = 0; + break; + } + + nbytes += t; + t = 0; + } + } + if(t != 0) { + kwrite(dst, buf, t); + nbytes += t; + } + acquire(); + free(buf); + *f->ret = nbytes; +} + +void +Sys_export(void *fp) +{ + F_Sys_export *f; + + f = fp; + release(); + *f->ret = export(fdchk(f->c), string2c(f->dir), f->flag&Sys_EXPASYNC); + acquire(); +} + +void +Sys_file2chan(void *fp) +{ + int r; + Heap *h; + Channel *c; + Sys_FileIO *fio; + F_Sys_file2chan *f; + void *sv; + + h = heap(TFileIO); + + fio = H2D(Sys_FileIO*, h); + + c = cnewc(FioTread, movtmp, 16); + fio->read = c; + + c = cnewc(FioTwrite, movtmp, 16); + fio->write = c; + + f = fp; + sv = *f->ret; + *f->ret = fio; + destroy(sv); + + release(); + r = srvf2c(string2c(f->dir), string2c(f->file), fio); + acquire(); + if(r == -1) { + *f->ret = H; + destroy(fio); + } +} + +enum +{ + /* the following pctl calls can block and must release the virtual machine */ + BlockingPctl= Sys_NEWFD|Sys_FORKFD|Sys_NEWNS|Sys_FORKNS|Sys_NEWENV|Sys_FORKENV +}; + +void +Sys_pctl(void *fp) +{ + int fd; + Prog *p; + List *l; + Chan *c; + volatile struct {Pgrp *np;} np; + Pgrp *opg; + Chan *dot; + Osenv *o; + F_Sys_pctl *f; + Fgrp *fg, *ofg, *nfg; + volatile struct {Egrp *ne;} ne; + Egrp *oe; + + f = fp; + + p = currun(); + if(f->flags & BlockingPctl) + release(); + + np.np = nil; + ne.ne = nil; + if(waserror()) { + closepgrp(np.np); + closeegrp(ne.ne); + if(f->flags & BlockingPctl) + acquire(); + *f->ret = -1; + return; + } + + o = p->osenv; + if(f->flags & Sys_NEWFD) { + ofg = o->fgrp; + nfg = newfgrp(ofg); + lock(&ofg->l); + /* file descriptors to preserve */ + for(l = f->movefd; l != H; l = l->tail) { + fd = *(int*)l->data; + if(fd >= 0 && fd <= ofg->maxfd) { + c = ofg->fd[fd]; + if(c != nil && fd < nfg->nfd && nfg->fd[fd] == nil) { + incref(&c->r); + nfg->fd[fd] = c; + if(nfg->maxfd < fd) + nfg->maxfd = fd; + } + } + } + unlock(&ofg->l); + o->fgrp = nfg; + closefgrp(ofg); + } + else + if(f->flags & Sys_FORKFD) { + ofg = o->fgrp; + fg = dupfgrp(ofg); + /* file descriptors to close */ + for(l = f->movefd; l != H; l = l->tail) + kclose(*(int*)l->data); + o->fgrp = fg; + closefgrp(ofg); + } + + if(f->flags & Sys_NEWNS) { + np.np = newpgrp(); + dot = o->pgrp->dot; + np.np->dot = cclone(dot); + np.np->slash = cclone(dot); + cnameclose(np.np->slash->name); + np.np->slash->name = newcname("/"); + np.np->pin = o->pgrp->pin; /* pin is ALWAYS inherited */ + np.np->nodevs = o->pgrp->nodevs; + opg = o->pgrp; + o->pgrp = np.np; + np.np = nil; + closepgrp(opg); + } + else + if(f->flags & Sys_FORKNS) { + np.np = newpgrp(); + pgrpcpy(np.np, o->pgrp); + opg = o->pgrp; + o->pgrp = np.np; + np.np = nil; + closepgrp(opg); + } + + if(f->flags & Sys_NEWENV) { + oe = o->egrp; + o->egrp = newegrp(); + closeegrp(oe); + } + else + if(f->flags & Sys_FORKENV) { + ne.ne = newegrp(); + egrpcpy(ne.ne, o->egrp); + oe = o->egrp; + o->egrp = ne.ne; + ne.ne = nil; + closeegrp(oe); + } + + if(f->flags & Sys_NEWPGRP) + newgrp(p); + + if(f->flags & Sys_NODEVS) + o->pgrp->nodevs = 1; + + poperror(); + + if(f->flags & BlockingPctl) + acquire(); + + *f->ret = p->pid; +} + +void +Sys_dirread(void *fp) +{ + + Dir *b; + int i, n; + Heap *h; + uchar *d; + void *r; + F_Sys_dirread *f; + + f = fp; + f->ret->t0 = -1; + r = f->ret->t1; + f->ret->t1 = H; + destroy(r); + release(); + n = kdirread(fdchk(f->fd), &b); + acquire(); + if(n <= 0) { + f->ret->t0 = n; + free(b); + return; + } + if(waserror()){ + free(b); + return; + } + h = heaparray(Tdir, n); + poperror(); + d = H2D(Array*, h)->data; + for(i = 0; i < n; i++) { + unpackdir(b+i, (Sys_Dir*)d); + d += Sys_Dir_size; + } + f->ret->t0 = n; + f->ret->t1 = H2D(Array*, h); + free(b); +} + +void +Sys_fauth(void *fp) +{ + int fd; + F_Sys_fauth *f; + void *r; + + f = fp; + r = *f->ret; + *f->ret = H; + destroy(r); + release(); + fd = kfauth(fdchk(f->fd), string2c(f->aname)); + acquire(); + if(fd >= 0) + *f->ret = mkfd(fd); +} + +void +Sys_fversion(void *fp) +{ + void *r; + F_Sys_fversion *f; + int n; + char buf[20], *s; + + f = fp; + f->ret->t0 = -1; + r = f->ret->t1; + f->ret->t1 = H; + destroy(r); + s = string2c(f->version); + n = strlen(s); + if(n >= sizeof(buf)-1) + n = sizeof(buf)-1; + memmove(buf, s, n); + buf[n] = 0; + release(); + n = kfversion(fdchk(f->fd), f->msize, buf, sizeof(buf)); + acquire(); + if(n >= 0){ + f->ret->t0 = f->msize; + retnstr(buf, n, &f->ret->t1); + } +} + +void +Sys_iounit(void *fp) +{ + F_Sys_iounit *f; + + f = fp; + release(); + *f->ret = kiounit(fdchk(f->fd)); + acquire(); +} + +void +ccom(Progq **cl, Prog *p) +{ + volatile struct {Progq **cl;} vcl; + + cqadd(cl, p); + vcl.cl = cl; + if(waserror()) { + if(p->ptr != nil) { /* no killcomm */ + cqdelp(vcl.cl, p); + p->ptr = nil; + } + nexterror(); + } + cblock(p); + poperror(); +} + +void +crecv(Channel *c, void *ip) +{ + Prog *p; + REG rsav; + + if(c->send->prog == nil && c->size == 0) { + p = currun(); + p->ptr = ip; + ccom(&c->recv, p); + return; + } + + rsav = R; + R.s = &c; + R.d = ip; + irecv(); + R = rsav; +} + +void +csend(Channel *c, void *ip) +{ + Prog *p; + REG rsav; + + if(c->recv->prog == nil && (c->buf == H || c->size == c->buf->len)) { + p = currun(); + p->ptr = ip; + ccom(&c->send, p); + return; + } + + rsav = R; + R.s = ip; + R.d = &c; + isend(); + R = rsav; +} diff --git a/emu/port/ip.h b/emu/port/ip.h new file mode 100644 index 00000000..f4666f45 --- /dev/null +++ b/emu/port/ip.h @@ -0,0 +1,64 @@ +enum +{ + IPaddrlen = 16, /* IPv6 */ + IPv4addrlen = 4, /* IPv4 */ + IPv4off = 12, /* length of IPv6 prefix for IPv4 addresses */ + Udphdrlen = 3*IPaddrlen+2*2, + OUdphdrlen = 2*IPaddrlen+2*2, + OUdphdrlenv4 = 2*IPv4addrlen+2*2, + + S_TCP = 0, + S_UDP +}; + +typedef struct Fs Fs; +typedef struct Proto Proto; +typedef struct Conv Conv; + +extern int so_socket(int type); +extern void so_connect(int, unsigned long, unsigned short); +extern void so_getsockname(int, unsigned long*, unsigned short*); +extern void so_bind(int, int, unsigned short); +extern void so_listen(int); +extern int so_accept(int, unsigned long*, unsigned short*); +extern int so_getservbyname(char*, char*, char*); +extern int so_gethostbyname(char*, char**, int); +extern int so_gethostbyaddr(char*, char**, int); +extern int so_recv(int, void*, int, void*, int); +extern int so_send(int, void*, int, void*, int); +extern void so_close(int); +extern int so_hangup(int, int); +extern void so_setsockopt(int, int, int); +extern int so_mustbind(int, int); +extern void so_keepalive(int, int); + + +extern void hnputl(void *p, unsigned long v); +extern void hnputs(void *p, unsigned short v); +extern unsigned long nhgetl(void *p); +extern unsigned short nhgets(void *p); +extern unsigned long parseip(uchar *to, char *from); +extern int parsemac(uchar *to, char *from, int len); +extern char* v4parseip(uchar*, char*); +extern int bipipe(int[]); + +extern int isv4(uchar*); +extern void v4tov6(uchar *v6, uchar *v4); +extern int v6tov4(uchar *v4, uchar *v6); +extern int eipfmt(Fmt*); + +#define ipmove(x, y) memmove(x, y, IPaddrlen) +#define ipcmp(x, y) ( (x)[IPaddrlen-1] != (y)[IPaddrlen-1] || memcmp(x, y, IPaddrlen) ) + +extern uchar IPv4bcast[IPaddrlen]; +extern uchar IPv4bcastobs[IPaddrlen]; +extern uchar IPv4allsys[IPaddrlen]; +extern uchar IPv4allrouter[IPaddrlen]; +extern uchar IPnoaddr[IPaddrlen]; +extern uchar v4prefix[IPaddrlen]; +extern uchar IPallbits[IPaddrlen]; + +extern void arpadd(char*, char*, int); +extern int arpwrite(char*, int); + +extern int Fsproto(Fs*, Proto*); diff --git a/emu/port/ipaux.c b/emu/port/ipaux.c new file mode 100644 index 00000000..9a938331 --- /dev/null +++ b/emu/port/ipaux.c @@ -0,0 +1,525 @@ +#include "dat.h" +#include "fns.h" +#include "ip.h" +#include "error.h" + +/* + * well known IP addresses + */ +uchar IPv4bcast[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff +}; +uchar IPv4allsys[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0xff, 0xff, + 0xe0, 0, 0, 0x01 +}; +uchar IPv4allrouter[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0xff, 0xff, + 0xe0, 0, 0, 0x02 +}; +uchar IPallbits[IPaddrlen] = { + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff +}; + +uchar IPnoaddr[IPaddrlen]; + +/* + * prefix of all v4 addresses + */ +uchar v4prefix[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0xff, 0xff, + 0, 0, 0, 0 +}; + +/* + * well known IPv6 addresses + */ +uchar v6Unspecified[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +uchar v6loopback[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x01 +}; +uchar v6linklocal[IPaddrlen] = { + 0xfe, 0x80, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +uchar v6linklocalmask[IPaddrlen] = { + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +int v6linklocalprefix = 8; +uchar v6sitelocal[IPaddrlen] = { + 0xfe, 0xc0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +uchar v6sitelocalmask[IPaddrlen] = { + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +int v6sitelocalprefix = 6; +uchar v6glunicast[IPaddrlen] = { + 0x08, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +uchar v6multicast[IPaddrlen] = { + 0xff, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +uchar v6multicastmask[IPaddrlen] = { + 0xff, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +int v6multicastprefix = 1; +uchar v6allnodesN[IPaddrlen] = { + 0xff, 0x01, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x01 +}; +uchar v6allnodesNmask[IPaddrlen] = { + 0xff, 0xff, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +int v6allnodesprefix = 2; +uchar v6allnodesL[IPaddrlen] = { + 0xff, 0x02, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x01 +}; +uchar v6allnodesLmask[IPaddrlen] = { + 0xff, 0xff, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +int v6allnodesLprefix = 2; +uchar v6allroutersN[IPaddrlen] = { + 0xff, 0x01, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x02 +}; +uchar v6allroutersL[IPaddrlen] = { + 0xff, 0x02, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x02 +}; +uchar v6allroutersS[IPaddrlen] = { + 0xff, 0x05, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x02 +}; +uchar v6solicitednode[IPaddrlen] = { + 0xff, 0x02, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x01, + 0xff, 0, 0, 0 +}; +uchar v6solicitednodemask[IPaddrlen] = { + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0x0, 0x0, 0x0 +}; +int v6solicitednodeprefix = 13; + +enum +{ + Isprefix= 16, +}; + +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 *lp; + ushort s; + int i, j, n, eln, eli, m, v; + + switch(f->r) { + case 'E': /* Ethernet address */ + p = va_arg(f->args, uchar*); + return fmtprint(f, efmt, p[0], p[1], p[2], p[3], p[4], p[5]); + case 'I': /* Ip address */ + p = va_arg(f->args, uchar*); +common: + if(memcmp(p, v4prefix, 12) == 0) + return fmtprint(f, ifmt, p[12], p[13], p[14], p[15]); + + /* find longest elision */ + eln = eli = -1; + for(i = 0; i < 16; i += 2){ + for(j = i; j < 16; j += 2) + if(p[j] != 0 || p[j+1] != 0) + break; + if(j > i && j - i > eln){ + eli = i; + eln = j - i; + } + } + + /* print with possible elision */ + n = 0; + for(i = 0; i < 16; i += 2){ + if(i == eli){ + n += sprint(buf+n, "::"); + i += eln; + if(i >= 16) + break; + } else if(i != 0) + n += sprint(buf+n, ":"); + s = (p[i]<<8) + p[i+1]; + n += sprint(buf+n, "%ux", s); + } + return fmtstrcpy(f, buf); + + case 'i': /* v6 address as 4 longs */ + lp = va_arg(f->args, ulong*); + for(i = 0; i < 4; i++) + hnputl(ip+4*i, *lp++); + p = ip; + goto common; + + case 'V': /* v4 ip address */ + p = va_arg(f->args, uchar*); + return fmtprint(f, ifmt, p[0], p[1], p[2], p[3]); + + case 'M': /* ip mask */ + p = va_arg(f->args, uchar*); + + /* look for a prefix mask */ + for(i = 0; i < 16; i++) + if(p[i] != 0xff) + break; + for(j = i+1; j < 16; j++) + if(p[j] != 0) + goto common; + n = 8*i; + if(i < IPaddrlen){ + v = p[i]; + for(m = 0x80; m != 0; m >>= 1){ + if((v & m) == 0) + break; + v &= ~m; + n++; + } + if(v != 0) + goto common; + } + + /* got one, use /xx format */ + return fmtprint(f, "/%d", n); + + } + return fmtstrcpy(f, "(eipfmt)"); +} + +#define CLASS(p) ((*(uchar*)(p))>>6) + +char* +v4parseip(uchar *to, char *from) +{ + int i; + char *p; + + p = from; + for(i = 0; i < 4 && *p; i++){ + to[i] = strtoul(p, &p, 0); + if(*p == '.') + p++; + } + switch(CLASS(to)){ + case 0: /* class A - 1 uchar net */ + case 1: + if(i == 3){ + to[3] = to[2]; + to[2] = to[1]; + to[1] = 0; + } else if(i == 2){ + to[3] = to[1]; + to[1] = 0; + } + break; + case 2: /* class B - 2 uchar net */ + if(i == 3){ + to[3] = to[2]; + to[2] = 0; + } + break; + } + return p; +} + +int +isv4(uchar *ip) +{ + return memcmp(ip, v4prefix, IPv4off) == 0; +} + + +/* + * the following routines are unrolled with no memset's to speed + * up the usual case + */ +void +v4tov6(uchar *v6, uchar *v4) +{ + v6[0] = 0; + v6[1] = 0; + v6[2] = 0; + v6[3] = 0; + v6[4] = 0; + v6[5] = 0; + v6[6] = 0; + v6[7] = 0; + v6[8] = 0; + v6[9] = 0; + v6[10] = 0xff; + v6[11] = 0xff; + v6[12] = v4[0]; + v6[13] = v4[1]; + v6[14] = v4[2]; + v6[15] = v4[3]; +} + +int +v6tov4(uchar *v4, uchar *v6) +{ + if(v6[0] == 0 + && v6[1] == 0 + && v6[2] == 0 + && v6[3] == 0 + && v6[4] == 0 + && v6[5] == 0 + && v6[6] == 0 + && v6[7] == 0 + && v6[8] == 0 + && v6[9] == 0 + && v6[10] == 0xff + && v6[11] == 0xff) + { + v4[0] = v6[12]; + v4[1] = v6[13]; + v4[2] = v6[14]; + v4[3] = v6[15]; + return 0; + } else { + memset(v4, 0, 4); + return -1; + } +} + +ulong +parseip(uchar *to, char *from) +{ + int i, elipsis = 0, v4 = 1; + ulong x; + char *p, *op; + + memset(to, 0, IPaddrlen); + p = from; + for(i = 0; i < 16 && *p; i+=2){ + op = p; + x = strtoul(p, &p, 16); + if(*p == '.' || (*p == 0 && i == 0)){ + p = v4parseip(to+i, op); + i += 4; + break; + } else { + to[i] = x>>8; + to[i+1] = x; + } + if(*p == ':'){ + v4 = 0; + if(*++p == ':'){ + elipsis = i+2; + p++; + } + } + } + if(i < 16){ + memmove(&to[elipsis+16-i], &to[elipsis], i-elipsis); + memset(&to[elipsis], 0, 16-i); + } + if(v4){ + to[10] = to[11] = 0xff; + return nhgetl(to+12); + } else + return 6; +} + +/* + * hack to allow ip v4 masks to be entered in the old + * style + */ +ulong +parseipmask(uchar *to, char *from) +{ + ulong x; + int i; + uchar *p; + + if(*from == '/'){ + /* as a number of prefix bits */ + i = atoi(from+1); + if(i < 0) + i = 0; + if(i > 128) + i = 128; + memset(to, 0, IPaddrlen); + for(p = to; i >= 8; i -= 8) + *p++ = 0xff; + if(i > 0) + *p = ~((1<<(8-i))-1); + x = nhgetl(to+IPv4off); + } else { + /* as a straight bit mask */ + x = parseip(to, from); + if(memcmp(to, v4prefix, IPv4off) == 0) + memset(to, 0xff, IPv4off); + } + return x; +} + +void +maskip(uchar *from, uchar *mask, uchar *to) +{ + int i; + + for(i = 0; i < IPaddrlen; i++) + to[i] = from[i] & mask[i]; +} + +uchar classmask[4][16] = { + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0x00,0x00,0x00, + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0x00,0x00,0x00, + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0x00,0x00, + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0x00, +}; + +uchar* +defmask(uchar *ip) +{ + if(isv4(ip)) + return classmask[ip[IPv4off]>>6]; + else { + if(ipcmp(ip, v6loopback) == 0) + return IPallbits; + else if(memcmp(ip, v6linklocal, v6linklocalprefix) == 0) + return v6linklocalmask; + else if(memcmp(ip, v6sitelocal, v6sitelocalprefix) == 0) + return v6sitelocalmask; + else if(memcmp(ip, v6solicitednode, v6solicitednodeprefix) == 0) + return v6solicitednodemask; + else if(memcmp(ip, v6multicast, v6multicastprefix) == 0) + return v6multicastmask; + return IPallbits; + } +} + +/* + * parse a hex mac address + */ +int +parsemac(uchar *to, char *from, int len) +{ + char nip[4]; + char *p; + int i; + + p = from; + memset(to, 0, len); + for(i = 0; i < len; i++){ + if(p[0] == '\0' || p[1] == '\0') + break; + + nip[0] = p[0]; + nip[1] = p[1]; + nip[2] = '\0'; + p += 2; + + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return i; +} + +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); +} diff --git a/emu/port/ipif-posix.c b/emu/port/ipif-posix.c new file mode 100644 index 00000000..2502e183 --- /dev/null +++ b/emu/port/ipif-posix.c @@ -0,0 +1,415 @@ +#ifdef sun +#define uint uxuint +#define ulong uxulong +#define ushort uxushort +#endif +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <sys/ioctl.h> +#undef ulong +#undef ushort +#undef uint + +#include "dat.h" +#include "fns.h" +#include "ip.h" +#include "error.h" + +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(); + if(type == SOCK_DGRAM){ + one = 1; + setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*)&one, sizeof (one)); + }else{ + one = 1; + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)); + } + return fd; +} + +int +so_send(int sock, void *va, int len, void *hdr, int hdrlen) +{ + int r; + struct sockaddr sa; + struct sockaddr_in *sin; + char *h = hdr; + + + osenter(); + if(hdr == 0) + r = write(sock, va, len); + else { + memset(&sa, sizeof(sa), 0); + sin = (struct sockaddr_in*)&sa; + sin->sin_family = AF_INET; + switch(hdrlen){ + case OUdphdrlenv4: + memmove(&sin->sin_addr, h, 4); + memmove(&sin->sin_port, h+8, 2); + break; + case OUdphdrlen: + v6tov4((uchar*)&sin->sin_addr, h); + memmove(&sin->sin_port, h+2*IPaddrlen, 2); /* rport */ + break; + default: + v6tov4((uchar*)&sin->sin_addr, h); + memmove(&sin->sin_port, h+3*IPaddrlen, 2); + break; + } + r = sendto(sock, va, len, 0, &sa, sizeof(sa)); + } + osleave(); + return r; +} + +int +so_recv(int sock, void *va, int len, void *hdr, int hdrlen) +{ + int r, l; + struct sockaddr sa; + struct sockaddr_in *sin; + char h[Udphdrlen]; + + + osenter(); + if(hdr == 0) + r = read(sock, va, len); + else { + sin = (struct sockaddr_in*)&sa; + l = sizeof(sa); + r = recvfrom(sock, va, len, 0, &sa, &l); + if(r >= 0) { + memset(h, sizeof h, 0); + switch(hdrlen){ + case OUdphdrlenv4: + memmove(h, &sin->sin_addr, 4); + memmove(h+2*IPv4addrlen, &sin->sin_port, 2); + break; + case OUdphdrlen: + v4tov6(h, (uchar*)&sin->sin_addr); + memmove(h+2*IPaddrlen, &sin->sin_port, 2); + break; + default: + v4tov6(h, (uchar*)&sin->sin_addr); + memmove(h+3*IPaddrlen, &sin->sin_port, 2); + break; + } + + /* alas there's no way to get the local addr/port correctly. Pretend. */ + getsockname(sock, &sa, &l); + switch(hdrlen){ + case OUdphdrlenv4: + memmove(h+IPv4addrlen, &sin->sin_addr, IPv4addrlen); + memmove(h+2*IPv4addrlen+2, &sin->sin_port, 2); + break; + case OUdphdrlen: + v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr); + memmove(h+2*IPaddrlen+2, &sin->sin_port, 2); + break; + default: + v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr); + v4tov6(h+2*IPaddrlen, (uchar*)&sin->sin_addr); /* ifcaddr */ + memmove(h+3*IPaddrlen+2, &sin->sin_port, 2); + break; + } + memmove(hdr, h, hdrlen); + } + } + osleave(); + return r; +} + +void +so_close(int sock) +{ + close(sock); +} + +void +so_connect(int fd, unsigned long raddr, unsigned short rport) +{ + int r; + struct sockaddr sa; + struct sockaddr_in *sin; + + memset(&sa, 0, sizeof(sa)); + sin = (struct sockaddr_in*)&sa; + sin->sin_family = AF_INET; + hnputs(&sin->sin_port, rport); + hnputl(&sin->sin_addr.s_addr, raddr); + + osenter(); + r = connect(fd, &sa, sizeof(sa)); + osleave(); + if(r < 0) + oserror(); +} + +void +so_getsockname(int fd, unsigned long *laddr, unsigned short *lport) +{ + int len; + struct sockaddr sa; + struct sockaddr_in *sin; + + len = sizeof(sa); + if(getsockname(fd, &sa, &len) < 0) + oserror(); + + sin = (struct sockaddr_in*)&sa; + 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) +{ + int r; + + osenter(); + r = listen(fd, 5); + osleave(); + if(r < 0) + oserror(); +} + +int +so_accept(int fd, unsigned long *raddr, unsigned short *rport) +{ + int nfd, len; + struct sockaddr sa; + struct sockaddr_in *sin; + + sin = (struct sockaddr_in*)&sa; + + len = sizeof(sa); + osenter(); + nfd = accept(fd, &sa, &len); + osleave(); + 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 sa; + struct sockaddr_in *sin; + + sin = (struct sockaddr_in*)&sa; + + one = 1; + if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0) { + oserrstr(up->genbuf, sizeof(up->genbuf)); + print("setsockopt: %s", up->genbuf); + } + + if(su) { + for(i = 600; i < 1024; i++) { + memset(&sa, 0, sizeof(sa)); + sin->sin_family = AF_INET; + hnputs(&sin->sin_port, i); + + if(bind(fd, &sa, sizeof(sa)) >= 0) + return; + } + oserror(); + } + + memset(&sa, 0, sizeof(sa)); + sin->sin_family = AF_INET; + hnputs(&sin->sin_port, port); + + if(bind(fd, &sa, sizeof(sa)) < 0) + oserror(); +} + +void +so_setsockopt(int fd, int opt, int value) +{ + int r; + struct linger l; + + if(opt == SO_LINGER){ + l.l_onoff = 1; + l.l_linger = (short) value; + osenter(); + r = setsockopt(fd, SOL_SOCKET, opt, (char *)&l, sizeof(l)); + osleave(); + }else + error(Ebadctl); + if(r < 0) + oserror(); +} + +int +so_gethostbyname(char *host, char**hostv, int n) +{ + int i; + unsigned char buf[32], *p; + struct hostent *hp; + + hp = gethostbyname(host); + if(hp == 0) + return 0; + + for(i = 0; hp->h_addr_list[i] && i < n; i++) { + p = hp->h_addr_list[i]; + sprint(buf, "%ud.%ud.%ud.%ud", p[0], p[1], p[2], p[3]); + hostv[i] = strdup(buf); + if(hostv[i] == 0) + break; + } + return i; +} + +int +so_gethostbyaddr(char *addr, char **hostv, int n) +{ + int i; + struct hostent *hp; + unsigned long straddr; + + straddr = inet_addr(addr); + if(straddr == -1) + return 0; + + hp = gethostbyaddr((char *)&straddr, sizeof(straddr), AF_INET); + if(hp == 0) + return 0; + + hostv[0] = strdup(hp->h_name); + if(hostv[0] == 0) + return 0; + for(i = 1; hp->h_aliases[i-1] && i < n; i++) { + hostv[i] = strdup(hp->h_aliases[i-1]); + if(hostv[i] == 0) + break; + } + return i; +} + +int +so_getservbyname(char *service, char *net, char *port) +{ + ushort p; + struct servent *s; + + s = getservbyname(service, net); + if(s == 0) + return -1; + p = s->s_port; + sprint(port, "%d", nhgets(&p)); + return 0; +} + +int +so_hangup(int fd, int linger) +{ + int r; + static struct linger l = {1, 1000}; + + osenter(); + if(linger) + setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&l, sizeof(l)); + r = shutdown(fd, 2); + if(r >= 0) + r = close(fd); + osleave(); + return r; +} + +void +arpadd(char *ipaddr, char *eaddr, int n) +{ +#ifdef SIOCGARP + struct arpreq a; + struct sockaddr_in pa; + int s; + uchar addr[IPaddrlen]; + + s = socket(AF_INET, SOCK_DGRAM, 0); + memset(&a, 0, sizeof(a)); + memset(&pa, 0, sizeof(pa)); + pa.sin_family = AF_INET; + pa.sin_port = 0; + parseip(addr, ipaddr); + if(!isv4(addr)){ + close(s); + error(Ebadarg); + } + memmove(&pa.sin_addr, ipaddr+IPv4off, IPv4addrlen); + memmove(&a.arp_pa, &pa, sizeof(pa)); + while(ioctl(s, SIOCGARP, &a) != -1) { + ioctl(s, SIOCDARP, &a); + memset(&a.arp_ha, 0, sizeof(a.arp_ha)); + } + a.arp_ha.sa_family = AF_UNSPEC; + parsemac((uchar*)a.arp_ha.sa_data, eaddr, 6); + a.arp_flags = ATF_PERM; + if(ioctl(s, SIOCSARP, &a) == -1) { + oserrstr(up->env->errstr, ERRMAX); + close(s); + error(up->env->errstr); + } + close(s); +#else + error("arp not implemented"); +#endif +} + +int +so_mustbind(int restricted, int port) +{ + return restricted || port != 0; +} + +void +so_keepalive(int fd, int ms) +{ + int on; + + on = 1; + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&on, sizeof(on)); +#ifdef TCP_KEEPIDLE + if(ms <= 120000) + ms = 120000; + ms /= 1000; + setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, (char*)&ms, sizeof(ms)); +#endif +} diff --git a/emu/port/keysym2ucs.h b/emu/port/keysym2ucs.h new file mode 100644 index 00000000..1f23ac66 --- /dev/null +++ b/emu/port/keysym2ucs.h @@ -0,0 +1,9 @@ +/* $XFree86: xc/programs/xterm/keysym2ucs.h,v 1.1 1999/06/12 15:37:18 dawes Exp $ */ +/* + * This module converts keysym values into the corresponding ISO 10646-1 + * (UCS, Unicode) values. + */ + +#include <X11/X.h> + +long keysym2ucs(KeySym keysym); diff --git a/emu/port/latin1.c b/emu/port/latin1.c new file mode 100644 index 00000000..9e8abf43 --- /dev/null +++ b/emu/port/latin1.c @@ -0,0 +1,83 @@ +#include "dat.h" + +/* + * The code makes two assumptions: strlen(ld) is 1 or 2; latintab[i].ld can be a + * prefix of latintab[j].ld only when j<i. + */ +static +struct cvlist +{ + char *ld; /* must be seen before using this conversion */ + char *si; /* options for last input characters */ + char *so; /* the corresponding Rune for each si entry */ +} latintab[] = { +#include "latin1.h" + 0, 0, 0 +}; + +/* + * Given 5 characters k[0]..k[4], find the rune or return -1 for failure. + */ +static long +unicode(uchar *k) +{ + long i, c; + + k++; /* skip 'X' */ + c = 0; + for(i=0; i<4; i++,k++){ + c <<= 4; + if('0'<=*k && *k<='9') + c += *k-'0'; + else if('a'<=*k && *k<='f') + c += 10 + *k-'a'; + else if('A'<=*k && *k<='F') + c += 10 + *k-'A'; + else + return -1; + } + return c; +} + +/* + * Given n characters k[0]..k[n-1], find the corresponding rune or return -1 for + * failure, or something < -1 if n is too small. In the latter case, the result + * is minus the required n. + */ +long +latin1(uchar *k, int n) +{ + struct cvlist *l; + int c; + char* p; + + if(k[0] == 'X') + if(n>=5) + return unicode(k); + else + return -5; + for(l=latintab; l->ld!=0; l++) + if(k[0] == l->ld[0]){ + if(n == 1) + return -2; + if(l->ld[1] == 0) + c = k[1]; + else if(l->ld[1] != k[1]) + continue; + else if(n == 2) + return -3; + else + c = k[2]; + for(p=l->si; *p!=0; p++) + if(*p == c) { + Rune r; + int i = p - l->si; + p = l->so; + for(; i >= 0; i--) + p += chartorune(&r, p); + return r; + } + return -1; + } + return -1; +} diff --git a/emu/port/latin1.h b/emu/port/latin1.h new file mode 100644 index 00000000..8ebab538 --- /dev/null +++ b/emu/port/latin1.h @@ -0,0 +1,99 @@ + " ", " i", "␣ı", + "!~", "-=~", "≄≇≉", + "!", "!<=>?bmp", "¡≮≠≯‽⊄∉⊅", + "\"*", "IUiu", "ΪΫϊϋ", + "\"", "\"AEIOUYaeiouy", "¨ÄËÏÖÜŸäëïöüÿ", + "$*", "fhk", "ϕϑϰ", + "$", "BEFHILMRVaefglopv", "ℬℰℱℋℐℒℳℛƲɑℯƒℊℓℴ℘ʋ", + "\'\"", "Uu", "Ǘǘ", + "\'", "\'ACEILNORSUYZacegilnorsuyz", "´ÁĆÉÍĹŃÓŔŚÚÝŹáćéģíĺńóŕśúýź", + "*", "*ABCDEFGHIKLMNOPQRSTUWXYZabcdefghiklmnopqrstuwxyz", "∗ΑΒΞΔΕΦΓΘΙΚΛΜΝΟΠΨΡΣΤΥΩΧΗΖαβξδεφγθικλμνοπψρστυωχηζ", + "+", "-O", "±⊕", + ",", ",ACEGIKLNORSTUacegiklnorstu", "¸ĄÇĘĢĮĶĻŅǪŖŞŢŲąçęģįķļņǫŗşţų", + "-*", "l", "ƛ", + "-", "+-2:>DGHILOTZbdghiltuz~", "∓ƻ÷→ÐǤĦƗŁ⊖ŦƵƀðǥℏɨłŧʉƶ≂", + ".", ".CEGILOZceglz", "·ĊĖĠİĿ⊙Żċėġŀż", + "/", "Oo", "Øø", + "1", "234568", "½⅓¼⅕⅙⅛", + "2", "-35", "ƻ⅔⅖", + "3", "458", "¾⅗⅜", + "4", "5", "⅘", + "5", "68", "⅚⅝", + "7", "8", "⅞", + ":", ")-=", "☺÷≔", + "<!", "=~", "≨⋦", + "<", "-<=>~", "←«≤≶≲", + "=", ":<=>OV", "≕⋜≡⋝⊜⇒", + ">!", "=~", "≩⋧", + ">", "<=>~", "≷≥»≳", + "?", "!?", "‽¿", + "@\'", "\'", "ъ", + "@@", "\'EKSTYZekstyz", "ьЕКСТЫЗекстыз", + "@C", "Hh", "ЧЧ", + "@E", "Hh", "ЭЭ", + "@K", "Hh", "ХХ", + "@S", "CHch", "ЩШЩШ", + "@T", "Ss", "ЦЦ", + "@Y", "AEOUaeou", "ЯЕЁЮЯЕЁЮ", + "@Z", "Hh", "ЖЖ", + "@c", "h", "ч", + "@e", "h", "э", + "@k", "h", "х", + "@s", "ch", "щш", + "@t", "s", "ц", + "@y", "aeou", "яеёю", + "@z", "h", "ж", + "@", "ABDFGIJLMNOPRUVXabdfgijlmnopruvx", "АБДФГИЙЛМНОПРУВХабдфгийлмнопрувх", + "A", "E", "Æ", + "C", "ACU", "⋂ℂ⋃", + "Dv", "Zz", "DŽDž", + "D", "-e", "Ð∆", + "G", "-", "Ǥ", + "H", "-H", "Ħℍ", + "I", "-J", "ƗIJ", + "L", "&-Jj|", "⋀ŁLJLj⋁", + "N", "JNj", "NJℕNj", + "O", "*+-./=EIcoprx", "⊛⊕⊖⊙⊘⊜ŒƢ©⊚℗®⊗", + "P", "P", "ℙ", + "Q", "Q", "ℚ", + "R", "R", "ℝ", + "S", "123S", "¹²³§", + "T", "-u", "Ŧ⊨", + "V", "=", "⇐", + "Y", "R", "Ʀ", + "Z", "-ACSZ", "Ƶℤ", + "^", "ACEGHIJOSUWYaceghijosuwy", "ÂĈÊĜĤÎĴÔŜÛŴŶâĉêĝĥîĵôŝûŵŷ", + "_\"", "AUau", "ǞǕǟǖ", + "_,", "Oo", "Ǭǭ", + "_.", "Aa", "Ǡǡ", + "_", "AEIOU_aeiou", "ĀĒĪŌŪ¯āēīōū", + "`\"", "Uu", "Ǜǜ", + "`", "AEIOUaeiou", "ÀÈÌÒÙàèìòù", + "a", "ben", "↔æ∠", + "b", "()+-0123456789=bknpqru", "₍₎₊₋₀₁₂₃₄₅₆₇₈₉₌♝♚♞♟♛♜•", + "c", "$Oagu", "¢©∩≅∪", + "dv", "z", "dž", + "d", "-adegz", "ð↓‡°†ʣ", + "e", "$lmns", "€⋯—–∅", + "f", "a", "∀", + "g", "$-r", "¤ǥ∇", + "h", "-v", "ℏƕ", + "i", "-bfjps", "ɨ⊆∞ij⊇∫", + "l", "\"$&\'-jz|", "“£∧‘łlj⋄∨", + "m", "iou", "µ∈×", + "n", "jo", "nj¬", + "o", "AOUaeiu", "Å⊚Ůåœƣů", + "p", "Odgrt", "℗∂¶∏∝", + "r", "\"\'O", "”’®", + "s", "()+-0123456789=abnoprstu", "⁽⁾⁺⁻⁰ⁱ⁴⁵⁶⁷⁸⁹⁼ª⊂ⁿº⊃√ß∍∑", + "t", "-efmsu", "ŧ∃∴™ς⊢", + "u", "-AEGIOUaegiou", "ʉĂĔĞĬŎŬ↑ĕğĭŏŭ", + "v\"", "Uu", "Ǚǚ", + "v", "ACDEGIKLNORSTUZacdegijklnorstuz", "ǍČĎĚǦǏǨĽŇǑŘŠŤǓŽǎčďěǧǐǰǩľňǒřšťǔž", + "w", "bknpqr", "♗♔♘♙♕♖", + "x", "O", "⊗", + "y", "$", "¥", + "z", "-", "ƶ", + "|", "Pp|", "Þþ¦", + "~!", "=", "≆", + "~", "-=AINOUainou~", "≃≅ÃĨÑÕŨãĩñõũ≈", diff --git a/emu/port/lock.c b/emu/port/lock.c new file mode 100644 index 00000000..48b5d8c2 --- /dev/null +++ b/emu/port/lock.c @@ -0,0 +1,141 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +void +lock(Lock *l) +{ + int i; + + if(_tas(&l->val) == 0) + return; + for(i=0; i<100; i++){ + if(_tas(&l->val) == 0) + return; + osyield(); + } + for(i=1;; i++){ + if(_tas(&l->val) == 0) + return; + osmillisleep(i*10); + if(i > 100){ + osyield(); + i = 1; + } + } +} + +int +canlock(Lock *l) +{ + return _tas(&l->val) == 0; +} + +void +unlock(Lock *l) +{ + l->val = 0; +} + +void +qlock(QLock *q) +{ + Proc *p; + + lock(&q->use); + if(!q->locked) { + q->locked = 1; + unlock(&q->use); + return; + } + p = q->tail; + if(p == 0) + q->head = up; + else + p->qnext = up; + q->tail = up; + up->qnext = 0; + unlock(&q->use); + osblock(); +} + +int +canqlock(QLock *q) +{ + if(!canlock(&q->use)) + return 0; + if(q->locked){ + unlock(&q->use); + return 0; + } + q->locked = 1; + unlock(&q->use); + return 1; +} + +void +qunlock(QLock *q) +{ + Proc *p; + + lock(&q->use); + p = q->head; + if(p) { + q->head = p->qnext; + if(q->head == 0) + q->tail = 0; + unlock(&q->use); + osready(p); + return; + } + q->locked = 0; + unlock(&q->use); +} + +void +rlock(RWlock *l) +{ + qlock(&l->x); /* wait here for writers and exclusion */ + lock(&l->l); + l->readers++; + canqlock(&l->k); /* block writers if we are the first reader */ + unlock(&l->l); + qunlock(&l->x); +} + +/* same as rlock but punts if there are any writers waiting */ +int +canrlock(RWlock *l) +{ + if (!canqlock(&l->x)) + return 0; + lock(&l->l); + l->readers++; + canqlock(&l->k); /* block writers if we are the first reader */ + unlock(&l->l); + qunlock(&l->x); + return 1; +} + +void +runlock(RWlock *l) +{ + lock(&l->l); + if(--l->readers == 0) /* last reader out allows writers */ + qunlock(&l->k); + unlock(&l->l); +} + +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/emu/port/main.c b/emu/port/main.c new file mode 100644 index 00000000..64f87f4e --- /dev/null +++ b/emu/port/main.c @@ -0,0 +1,433 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "interp.h" +#include "kernel.h" +#include "draw.h" +#include "version.h" + +int rebootargc = 0; +char** rebootargv; +static char *imod = "/dis/emuinit.dis"; +extern char* hosttype; +char* tkfont; /* for libtk/utils.c */ +int tkstylus; /* libinterp/tk.c */ +extern int mflag; + int dflag; + int vflag; + int vflag; + Procs procs; + char *eve; + int Xsize = 640; + int Ysize = 480; + int sflag; + int qflag; + int xtblbit; + int globfs; + ulong displaychan; +char *cputype; + +static void +usage(void) +{ + fprint(2, "Usage: emu [options...] [file.dis [args...]]\n" + "\t-gXxY\n" + "\t-c[0-9]\n" + "\t-b\n" + "\t-d file.dis\n" + "\t-s\n" + "\t-v\n" + "\t-p<poolname>=maxsize\n" + "\t-f<fontpath>\n" + "\t-r<rootpath>\n" + "\t-7\n" + "\t-G\n" + "\t-C<channel string>\n" + "\t-S\n"); + + exits("usage"); +} + +static void +envusage(void) +{ + fprint(2, "emu: bad option in EMU environment variable (%s)\n", getenv("EMU")); + usage(); +} + +static int +isnum(char *p) +{ + if (*p == 0) return 0; + while (*p) { + if (*p < '0' || *p > '9') return 0; + p++; + } + return 1; +} + +static int +geom(char *val) +{ + char *p; + int x, y; + if (val == '\0' || (*val < '0' || *val > '9')) + return 0; + x = strtoul(val, &p, 0); + if(x >= 64) + Xsize = x; + if (*p++ != 'x' || !isnum(p)) + return 0; + y = strtoul(p, &p, 0); + if(y >= 48) + Ysize = y; + if (*p != '\0') return 0; + return 1; +} + +static void +poolopt(char *str) +{ + char *var; + int n; + ulong x; + + var = str; + while(*str && *str != '=') + str++; + if(*str != '=' || str[1] == '\0') + usage(); + *str++ = '\0'; + n = strlen(str); + x = atoi(str); + switch(str[n - 1]){ + case 'k': + case 'K': + x *= 1024; + break; + case 'm': + case 'M': + x *= 1024*1024; + break; + } + if(poolsetsize(var, x) == 0) + usage(); +} + +static void +option(int argc, char *argv[], void (*badusage)(void)) +{ + char *cp; + + ARGBEGIN { + default: + badusage(); + case 'g': /* Window geometry */ + if (geom(EARGF(badusage())) == 0) + badusage(); + break; + case 'b': /* jit array bounds checking */ + bflag = 1; + break; + case 'c': /* Compile on the fly */ + cp = EARGF(badusage()); + if (!isnum(cp)) + badusage(); + cflag = atoi(cp); + if(cflag < 0|| cflag > 9) + usage(); + break; + case 'I': /* (temporary option) run without cons */ + dflag++; + break; + case 'd': /* run as a daemon */ + dflag++; + imod = EARGF(badusage()); + break; + case 's': /* No trap handling */ + sflag++; + break; + case 'm': /* gc mark and sweep */ + cp = EARGF(badusage()); + if (!isnum(cp)) + badusage(); + mflag = atoi(cp); + if(mflag < 0|| mflag > 9) + usage(); + break; + case 'p': /* pool option */ + poolopt(EARGF(badusage())); + break; + case 'f': /* Set font path */ + tkfont = EARGF(badusage()); + break; + case 'r': /* Set inferno root */ + strncpy(rootdir, EARGF(badusage()), sizeof(rootdir)-1); + break; + case '7': /* use 7 bit colormap in X */ + xtblbit = 1; + break; + case 'G': /* allow global access to file system */ + globfs = 1; + break; + case 'C': /* channel specification for display */ + cp = EARGF(badusage()); + displaychan = strtochan(cp); + if(displaychan == 0){ + fprint(2, "emu: invalid channel specifier (-C): %q\n", cp); + exits("usage"); + } + break; + case 'S': + tkstylus = 1; + break; + case 'v': + vflag = 1; /* print startup messages */ + break; + } ARGEND +} + +static void +savestartup(int argc, char *argv[]) +{ + int i; + + rebootargc = argc; + rebootargv = malloc((argc+1)*sizeof(char*)); + if(rebootargv == nil) + panic("can't save startup args"); + for(i = 0; i < argc; i++) { + rebootargv[i] = strdup(argv[i]); + if(rebootargv[i] == nil) + panic("can't save startup args"); + } + rebootargv[i] = nil; +} + +void +putenvq(char *name, char *val, int conf) +{ + val = smprint("%q", val); + ksetenv(name, val, conf); + free(val); +} + +void +putenvqv(char *name, char **v, int n, int conf) +{ + Fmt f; + int i; + char *val; + + fmtstrinit(&f); + for(i=0; i<n; i++) + fmtprint(&f, "%s%q", i?" ":"", v[i]); + val = fmtstrflush(&f); + ksetenv(name, val, conf); + free(val); +} + +void +main(int argc, char *argv[]) +{ + char *opt; + char *enva[20]; + int envc; + quotefmtinstall(); + savestartup(argc, argv); + opt = getenv("EMU"); + if(opt != nil && *opt != '\0') { + enva[0] = "emu"; + envc = tokenize(opt, &enva[1], sizeof(enva)-1) + 1; + enva[envc] = 0; + option(envc, enva, envusage); + } + option(argc, argv, usage); + eve = strdup("inferno"); + + opt = "interp"; + if(cflag) + opt = "compile"; + + if(vflag) + print("Inferno %s main (pid=%d) %s\n", VERSION, getpid(), opt); + + libinit(imod); +} + +void +emuinit(void *imod) +{ + Osenv *e; + + e = up->env; + e->pgrp = newpgrp(); + e->fgrp = newfgrp(nil); + e->egrp = newegrp(); + e->errstr = e->errbuf0; + e->syserrstr = e->errbuf1; + e->user = strdup(""); + + links(); + chandevinit(); + + if(waserror()) + panic("setting root and dot"); + + e->pgrp->slash = namec("#/", Atodir, 0, 0); + cnameclose(e->pgrp->slash->name); + e->pgrp->slash->name = newcname("/"); + e->pgrp->dot = cclone(e->pgrp->slash); + poperror(); + + strcpy(up->text, "main"); + + if(kopen("#c/cons", OREAD) != 0) + fprint(2, "failed to make fd0 from #c/cons: %r\n"); + kopen("#c/cons", OWRITE); + kopen("#c/cons", OWRITE); + + /* the setid cannot precede the bind of #U */ + kbind("#U", "/", MAFTER|MCREATE); + setid(eve, 0); + kbind("#^", "/dev", MBEFORE); /* snarf */ + kbind("#^", "/chan", MBEFORE); + kbind("#m", "/dev", MBEFORE); /* pointer */ + kbind("#c", "/dev", MBEFORE); + kbind("#p", "/prog", MREPL); + kbind("#d", "/fd", MREPL); + kbind("#I", "/net", MAFTER); /* will fail on Plan 9 */ + + /* BUG: we actually only need to do these on Plan 9 */ + kbind("#U/dev", "/dev", MAFTER); + kbind("#U/net", "/net", MAFTER); + kbind("#U/net.alt", "/net.alt", MAFTER); + + if(cputype != nil) + ksetenv("cputype", cputype, 1); + putenvqv("emuargs", rebootargv, rebootargc, 1); + putenvq("emuroot", rootdir, 1); + ksetenv("emuhost", hosttype, 1); + + kproc("main", disinit, imod, KPDUPFDG|KPDUPPG|KPDUPENVG); + + for(;;) + ospause(); +} + +void +errorf(char *fmt, ...) +{ + va_list arg; + char buf[PRINTSIZE]; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + error(buf); +} + +void +error(char *err) +{ + if(err != up->env->errstr && up->env->errstr != nil) + kstrcpy(up->env->errstr, err, ERRMAX); +// ossetjmp(up->estack[NERR-1]); + nexterror(); +} + +void +exhausted(char *resource) +{ + char buf[64]; + int n; + + n = snprint(buf, sizeof(buf), "no free %s\n", resource); + iprint(buf); + buf[n-1] = 0; + error(buf); +} + +void +nexterror(void) +{ + oslongjmp(nil, up->estack[--up->nerr], 1); +} + +/* for dynamic modules - functions not macros */ + +void* +waserr(void) +{ + up->nerr++; + return up->estack[up->nerr-1]; +} + +void +poperr(void) +{ + up->nerr--; +} + +char* +enverror(void) +{ + return up->env->errstr; +} + +void +panic(char *fmt, ...) +{ + va_list arg; + char buf[512]; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + fprint(2, "panic: %s\n", buf); + if(sflag) + abort(); + + cleanexit(0); +} + +int +iprint(char *fmt, ...) +{ + + int n; + va_list va; + char buf[1024]; + + va_start(va, fmt); + n = vseprint(buf, buf+sizeof buf, fmt, va) - buf; + va_end(va); + + write(1, buf, n); + return 1; +} + +void +_assert(char *fmt) +{ + panic("assert failed: %s", fmt); +} + +void +sysfatal(char *fmt, ...) +{ + va_list arg; + char buf[64]; + + va_start(arg, fmt); + vsnprint(buf, sizeof(buf), fmt, arg); + va_end(arg); + panic("sysfatal: %s", buf); +} + +void +oserror(void) +{ + oserrstr(up->env->errstr, ERRMAX); + error(up->env->errstr); +} diff --git a/emu/port/mkdevc b/emu/port/mkdevc new file mode 100644 index 00000000..825c76b0 --- /dev/null +++ b/emu/port/mkdevc @@ -0,0 +1,99 @@ +$AWK ' +BEGIN{ + if(ARGC < 2) + exit +} + +/^$/{ + next; +} +/^#/{ + next; +} +collect && /^[^ \t]/{ + collect = 0; +} +collect && section ~ "dev"{ + dev[ndev++] = $1; +} +collect && section ~ "ip"{ + ip[nip++] = $1; +} +collect && section ~ "link"{ + link[nlink++] = $1; +} +collect && section ~ "mod"{ + mod[nmod++] = $1; +} +collect && section ~ "misc"{ + misc[nmisc++] = $1; +} +collect && section ~ "port"{ + port[nport++] = $0; +} +collect && section ~ "code"{ + code[ncode++] = $0; +} +$0 ~ /^[^ \t]/{ + if($0 ~ "(code|dev|ip|lib|link|mod|misc|port|root)"){ + section = $0; + collect = 1; + } + next; +} + +END{ + if(ARGC < 2) + exit "usage" + + printf "#include \"dat.h\"\n" + printf "#include \"fns.h\"\n" + printf "#include \"error.h\"\n" + printf "#include \"interp.h\"\n\n\n" + printf "#include \"%s.root.h\"\n\n", ARGV[1]; + + nildev = 8; + printf "ulong ndevs = %s;\n\n", ndev+nildev + for(i = 0; i < ndev; i++) + printf "extern Dev %sdevtab;\n", dev[i]; + printf "Dev* devtab[]={\n" + for(i = 0; i < ndev; i++) + printf "\t&%sdevtab,\n", dev[i]; + for(i = 0; i < nildev; i++) + printf("\tnil,\n"); + printf "\tnil,\n};\n\n"; + + + for(i = 0; i < nlink; i++) + printf "extern void %slink(void);\n", link[i]; + + printf "void links(void){\n"; + for(i = 0; i < nlink; i++) + printf "\t%slink();\n", link[i]; + printf "}\n\n"; + + for(i = 0; i < nmod; i++) + printf "extern void %smodinit(void);\n", mod[i]; + printf "void modinit(void){\n"; + for(i = 0; i < nmod; i++) + printf "\t%smodinit();\n",mod[i]; + printf "}\n\n"; + + if(nip){ + printf "#include \"../ip/ip.h\"\n"; + for(i = 0; i < nip; i++) + printf "extern void %sinit(Fs*);\n", ip[i]; + printf "void (*ipprotoinit[])(Fs*) = {\n"; + for(i = 0; i < nip; i++) + printf "\t%sinit,\n", ip[i]; + printf "\tnil,\n};\n\n"; + } + + for(i = 0; i < ncode; i++) + printf "%s\n", code[i]; + + printf "char* conffile = \"%s\";\n", ARGV[1]; + printf "ulong kerndate = KERNDATE;\n"; + + exit +}' $* diff --git a/emu/port/mkdevlist b/emu/port/mkdevlist new file mode 100644 index 00000000..eea5ae16 --- /dev/null +++ b/emu/port/mkdevlist @@ -0,0 +1,75 @@ +$AWK ' +BEGIN{ + var["init"] = "INIT="; + var["ip"] = "IP="; + var["lib"] = "LIBS="; + var["root"] = "ROOTFILES="; + infernoroot = ENVIRON["ROOT"]; +} +/^$/{ next; +} +/^#/{ next; +} +/^env/{ + inenv = 1; + next; +} +inenv != 0 && /^[ ]/{ + sub("^[ ]*", "", $0) + printf "%s\n", $0 + next +} + +/^(code|dev|init|ip|lib|link|mod|misc|port|root)/{ + inenv = 0; + type = $1; + next; +} +/^[^ ]/ { + inenv = 0; +} +type && /^[ ]/{ + if(type == "code") + next; + if(type == "root"){ + if (NF > 1) + file = $2; + else if ($1 == "/osinit.dis") + next; # handled via explicit dependency + else + file = $1; + if(rootfile[file] == 0){ + var[type] = var[type] " " infernoroot file; + rootfile[file]++; + } + next; + } + if(type == "init" || type == "lib"){ + var[type] = var[type] " " $1; + next; + } + file = $1 "'.$O'" + if(type == "port") + port[file]++; + else if(type == "dev") + obj["dev" file]++; + else if(type != "mod") + obj[file]++; + for(i = 2; i <= NF; i++){ + if($i !~ "^[+=-].*") + obj[$i "'.$O'"]++; + } + next; +} +END{ + x = "" + for(i in obj) + x = x " " i + printf "DEVS=%s\n", x; + x = "" + for(i in port) + x = x " " i + printf "PORT=%s\n", x + for(v in var) + printf "%s\n", var[v] +}' $* diff --git a/emu/port/mkroot b/emu/port/mkroot new file mode 100644 index 00000000..46f0a0b0 --- /dev/null +++ b/emu/port/mkroot @@ -0,0 +1,121 @@ +$AWK ' +BEGIN{ + if (ARGC < 2) + exit "usage"; + + conf = ARGV[1]; + infernoroot = ENVIRON["ROOT"]; + init = ENVIRON["INIT"]; + data2c = ENVIRON["DATA2C"]; + if(data2c == "") + data2c = "data2c" + nroot = 0; +} +/^$/{ + next; +} +/^#/{ + next; +} +collect && /^[^ \t]/{ + collect = 0; +} +collect && section ~ "root"{ + dst[nroot] = $1; + if (NF > 1) + src[nroot] = infernoroot $2; + else if (dst[nroot] == "/osinit.dis") + src[nroot] = infernoroot "/dis/" init ".dis"; + else + src[nroot] = infernoroot $1; + for(i=0; i<nroot; i++) + if(dst[i] == dst[nroot]) + break; + if(i == nroot) + nroot++; +} +$0 ~ /^[^ \t]/{ + if($0 ~ "(code|dev|ether|ip|lib|link|mod|misc|port|root|vga)"){ + section = $0; + collect = 1; + } + next; +} +END{ + rootdata = conf ".root.c"; + system("rm -f " rootdata); + print("/* Generated by mkroot */") >rootdata; + close(rootdata); + isdir[0] = 1; + dotdot[0] = 0; + qid = 1; + for (i = 0; i < nroot; i++) { + ncomp = split(dst[i], comp, "/"); + if (comp[1] != "" || ncomp < 2) + continue; + q = 0; + for (j = 2; j <= ncomp; j++) { + key = q "/" comp[j]; + if (walk[key] == 0) { + walk[key] = qid; + dotdot[qid] = q; + q = qid++; + name[q] = comp[j]; + if (j < ncomp) + isdir[q] = 1; + } + else + q = walk[key]; + } + if (system("test -d " src[i]) == 0) + isdir[q] = 1; + else { + if (system(data2c " root" q " <" src[i] " >>" rootdata) != 0) + exit 1; + print("extern unsigned char root" q "code[];"); + print("extern int root" q "len;"); + } + } + + x = 1; + sort[0] = 0; + unsort[0] = 0; + for (q = 0; q < qid; q++) { + if (isdir[q]) { + nchild[q] = 0; + for (q2 = 1; q2 < qid; q2++) { + if (dotdot[q2] == q) { + if (nchild[q]++ == 0) + child0[q] = x; + sort[q2] = x++; + unsort[sort[q2]] = q2; + } + } + } + } + + print("int rootmaxq = " qid ";"); + + print("Dirtab roottab[" qid "] = {"); + for (oq = 0; oq < qid; oq++) { + q = unsort[oq]; + if (!isdir[q]) + print("\t\"" name[q] "\",\t{" oq ", 0, QTFILE},\t", "0,\t0444,"); + else + print("\t\"" name[q] "\",\t{" oq ", 0, QTDIR},\t", "0,\t0555,"); + } + print("};"); + + print("Rootdata rootdata[" qid "] = {"); + for (oq = 0; oq < qid; oq++) { + q = unsort[oq]; + if (!isdir[q]) + print("\t" sort[dotdot[q]] ",\t", "root" q "code,\t", "0,\t", "&root" q "len,"); + else if (nchild[q]) + print("\t" sort[dotdot[q]] ",\t", "&roottab[" child0[q] "],\t", nchild[q] ",\tnil,"); + else + print("\t" sort[dotdot[q]] ",\t", "nil,\t", "0,\t", "nil,"); + } + print("};"); +} +' $1 >$1.root.h diff --git a/emu/port/parse.c b/emu/port/parse.c new file mode 100644 index 00000000..fc84d17b --- /dev/null +++ b/emu/port/parse.c @@ -0,0 +1,111 @@ +#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/emu/port/pgrp.c b/emu/port/pgrp.c new file mode 100644 index 00000000..b3d473f3 --- /dev/null +++ b/emu/port/pgrp.c @@ -0,0 +1,265 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +static Ref pgrpid; +static Ref mountid; + +Pgrp* +newpgrp(void) +{ + Pgrp *p; + + p = malloc(sizeof(Pgrp)); + if(p == nil) + error(Enomem); + p->r.ref = 1; + p->pgrpid = incref(&pgrpid); + p->pin = Nopin; + p->progmode = 0644; + p->privatemem = 0; + return p; +} + +void +closepgrp(Pgrp *p) +{ + Mhead **h, **e, *f, *next; + + if(p == nil || decref(&p->r) != 0) + return; + + 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); + cclose(p->slash); + cclose(p->dot); + 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 = malloc(sizeof(Mhead)); + if(mh == nil) { + runlock(&f->lock); + wunlock(&from->ns); + error(Enomem); + } + mh->from = f->from; + mh->r.ref = 1; + incref(&mh->from->r); + *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); + if(n == nil) { + runlock(&f->lock); + wunlock(&from->ns); + error(Enomem); + } + 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); + + to->pin = from->pin; + + to->slash = cclone(from->slash); + to->dot = cclone(from->dot); + to->nodevs = from->nodevs; + wunlock(&from->ns); +} + +Fgrp* +newfgrp(Fgrp *old) +{ + Fgrp *new; + int n; + + new = malloc(sizeof(Fgrp)); + if(new == nil) + error(Enomem); + new->r.ref = 1; + n = DELTAFD; + if(old != nil){ + lock(&old->l); + if(old->maxfd >= n) + n = (old->maxfd+1 + DELTAFD-1)/DELTAFD * DELTAFD; + new->maxfd = old->maxfd; + unlock(&old->l); + } + new->nfd = n; + new->fd = malloc(n*sizeof(Chan*)); + if(new->fd == nil){ + free(new); + error(Enomem); + } + return new; +} + +Fgrp* +dupfgrp(Fgrp *f) +{ + int i; + Chan *c; + Fgrp *new; + int n; + + new = malloc(sizeof(Fgrp)); + if(new == nil) + error(Enomem); + new->r.ref = 1; + lock(&f->l); + n = DELTAFD; + if(f->maxfd >= n) + n = (f->maxfd+1 + DELTAFD-1)/DELTAFD * DELTAFD; + new->nfd = n; + new->fd = malloc(n*sizeof(Chan*)); + if(new->fd == nil){ + unlock(&f->l); + free(new); + error(Enomem); + } + new->maxfd = f->maxfd; + new->minfd = f->minfd; + for(i = 0; i <= f->maxfd; i++) { + if(c = f->fd[i]){ + incref(&c->r); + new->fd[i] = c; + } + } + unlock(&f->l); + + return new; +} + +void +closefgrp(Fgrp *f) +{ + int i; + Chan *c; + + if(f != nil && decref(&f->r) == 0) { + 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 = malloc(sizeof(Mount)); + if(m == nil) + error(Enomem); + m->to = to; + m->head = mh; + incref(&to->r); + 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; + } +} + +void +closesigs(Skeyset *s) +{ + int i; + + if(s == nil || decref(&s->r) != 0) + return; + for(i=0; i<s->nkey; i++) + freeskey(s->keys[i]); + free(s); +} + +void +freeskey(Signerkey *key) +{ + if(key == nil || decref(&key->r) != 0) + return; + free(key->owner); + (*key->pkfree)(key->pk); + free(key); +} diff --git a/emu/port/portmkfile b/emu/port/portmkfile new file mode 100644 index 00000000..498d5674 --- /dev/null +++ b/emu/port/portmkfile @@ -0,0 +1,169 @@ +PORTHFILES=\ + $ROOT/$OBJDIR/include/lib9.h\ + $ROOT/include/fcall.h\ + $ROOT/include/interp.h\ + $ROOT/include/draw.h\ + ../port/error.h\ + ../port/dat.h\ + ../port/fns.h\ + +LIBNAMES=${LIBS:%=lib%.a} +LIBFILES=${LIBS:%=$ROOT/$SYSTARG/$OBJTYPE/lib/lib%.a} + +$ROOT/$SYSTARG/$OBJTYPE/lib/lib%.a:N: lib%.a + +%.$O: %.s + $AS $ASFLAGS $stem.s + +%.$O: %.S$MACOSINF + $AS $ASFLAGS $stem.S + +%.$O: %.c + $CC $CFLAGS $stem.c + +%.$O: ../port/%.c + $CC $CFLAGS -I. ../port/$stem.c + +%.$O: ../ip/%.c + $CC $CFLAGS -I. ../ip/$stem.c + +&.$O: $HFILES $PORTHFILES + +$INSTALLDIR/%: % + cp $stem $INSTALLDIR/$stem + +installall:V: install-$SHELLTYPE +all:V: default-$SHELLTYPE + +# Plan 9 only (requires the compiler) +acid:V: i$CONF.acid + +i$CONF.acid: i$CONF + { + for (i in `{srclist -ec -r $ROOT/ i$CONF}) { + echo '//FILE: ' $i + $CC -I. -I../port -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp '-DKERNDATE='$KERNDATE -a $i + } + echo 'include ("inferno");' + } >i$CONF.acid + +lib%.a:V: $SHELLTYPE-lib%.a + +rc-lib%.a nt-lib%.a:VQ: + echo '@{builtin cd' $ROOT/lib$stem '; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install}' + @{builtin cd $ROOT/lib$stem; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install} + +sh-lib%.a:VQ: + echo "(cd $ROOT/lib$stem ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install)" + (cd $ROOT/lib$stem; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install) + +%-rc %-nt:V: + for(i in $CONFLIST) + mk 'CONF='$i $stem + +%-sh:V: + for i in $CONFLIST + do + mk 'CONF='$i $stem + done + +clean:V: cleanconf-$SHELLTYPE + rm -f *.[$OS] *.root.[shc] errstr.h *.out *.obj + +cleanconf-sh:V: + for i in $CONFLIST $CLEANCONFLIST + do + rm -f $i.c [$OS].$i i$i i$i.* $i.ver + done + +cleanconf-rc:V: + for(i in $CONFLIST $CLEANCONFLIST) + rm -f $i.c [$OS].$i i$i i$i.* $i.ver + +cleanconf-nt:V: + for(i in $CONFLIST $CLEANCONFLIST) + rm -f $i.c [$OS].$i i$i i$i.* $i.ver $i.exe + +nuke-sh:QV: + for i in $LIBDIRS + do + echo "(cd $ROOT/lib$i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE nuke)" + (cd $ROOT/lib$i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE nuke) + done + +nuke-rc nuke-nt:QV: + for (i in $LIBDIRS) + { + echo '@{cd $ROOT/lib$i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE nuke}' + @{cd $ROOT/lib$i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE nuke} + } + +nuke:V: clean nuke-$SHELLTYPE + rm -f srv.h srvm.h + +$CONF.c: ../port/mkdevc $CONF + $SHELLNAME ../port/mkdevc $CONF > $CONF.c + +errstr.h: ../port/error.h + sed 's/extern //;s,;.*/\* , = ",;s, \*/,";,' < ../port/error.h > errstr.h + +../init/%.dis: ../init/%.b + cd ../init; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE $stem.dis + +$ROOT/libinterp/runt.h: + cd $ROOT/libinterp + mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE runt.h + +INTERP=$ROOT/include/interp.h +RUNT=$ROOT/libinterp/runt.h # for culling dependencies + +alloc.$O: ../../include/pool.h\ + $INTERP +devcons.$O: $ROOT/include/version.h +devdraw.$O: $ROOT/include/draw.h\ + $ROOT/include/memdraw.h\ + $ROOT/include/memlayer.h +devmem.$O: $INTERP +devpipe.$O: $INTERP +devprof.$O: $INTERP +devprog.$O: $ROOT/libinterp/runt.h\ + $INTERP +devsign.$O: $INTERP +devsign.$O: $ROOT/libkeyring/keys.h +devsrv.$O: $ROOT/libinterp/runt.h\ + $INTERP +devtk.$O: $INTERP +dis.$O: $INTERP +discall.$O: $INTERP +exception.$O: $INTERP +inferno.$O: $ROOT/libinterp/runt.h\ + $INTERP +devmnt.$O: ../../include/fcall.h +main.$O: $ROOT/include/version.h +main.$O: ../port/error.h\ + $INTERP +proc.$O: errstr.h\ + $INTERP +srv.$O: $INTERP +devroot.$O: errstr.h +latin1.$O: ../port/latin1.h +netif.$O: ../port/netif.h +devprog.$O: $RUNT +devsrv.$O: $RUNT +exception.$O: $RUNT +inferno.$O: $RUNT +ipif-$SYSTARG.$O devip.$O: ../port/ip.h + +devroot.$O: $CONF.root.h +$CONF.$O: $CONF.root.h +$CONF.root.c $CONF.root.h: $CONF ../port/mkroot $ROOTFILES + $SHELLNAME ../port/mkroot $CONF + +audio.c:N: ../port/audio-tbls.c +audio.c:N: ../port/audio.h + +srv.$O: srv.h srvm.h +srv.h srvm.h:D: $ROOT/module/srvrunt.b $ROOT/module/srv.m + rm -f $alltarget + limbo -a -I$ROOT/module $ROOT/module/srvrunt.b >srv.h + limbo -t Srv -I$ROOT/module $ROOT/module/srvrunt.b >srvm.h diff --git a/emu/port/print.c b/emu/port/print.c new file mode 100644 index 00000000..4d166506 --- /dev/null +++ b/emu/port/print.c @@ -0,0 +1,23 @@ +#include "dat.h" +#include "fns.h" + +static Lock fmtl; + +void +_fmtlock(void) +{ + lock(&fmtl); +} + +void +_fmtunlock(void) +{ + unlock(&fmtl); +} + +int +_efgfmt(Fmt *f) +{ + USED(f); + return -1; +} diff --git a/emu/port/proc.c b/emu/port/proc.c new file mode 100644 index 00000000..fccf1a36 --- /dev/null +++ b/emu/port/proc.c @@ -0,0 +1,239 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "interp.h" + +Proc* +newproc(void) +{ + Proc *p; + + p = malloc(sizeof(Proc)); + if(p == nil) + return nil; + + p->type = Unknown; + p->killed = 0; + p->swipend = 0; + p->env = &p->defenv; + kstrdup(&p->env->user, "*nouser"); + p->env->errstr = p->env->errbuf0; + p->env->syserrstr = p->env->errbuf1; + addprog(p); + + return p; +} + +void +Sleep(Rendez *r, int (*f)(void*), void *arg) +{ + lock(&up->rlock); + lock(&r->l); + + /* + * if interrupted or condition happened, never mind + */ + if(up->killed || f(arg)) { + unlock(&r->l); + }else{ + if(r->p != nil) + panic("double sleep pc=0x%lux %s[%lud] %s[%lud] r=0x%lux\n", getcallerpc(&r), r->p->text, r->p->pid, up->text, up->pid, r); + + r->p = up; + unlock(&r->l); + up->swipend = 0; + up->r = r; /* for swiproc */ + unlock(&up->rlock); + + osblock(); + + lock(&up->rlock); + up->r = nil; + } + + if(up->killed || up->swipend) { + up->killed = 0; + up->swipend = 0; + unlock(&up->rlock); + error(Eintr); + } + unlock(&up->rlock); +} + +int +Wakeup(Rendez *r) +{ + Proc *p; + + lock(&r->l); + p = r->p; + if(p != nil) { + r->p = nil; + osready(p); + } + unlock(&r->l); + return p != nil; +} + +void +swiproc(Proc *p, int interp) +{ + Rendez *r; + + if(p == nil) + return; + + /* + * Pull out of emu Sleep + */ + lock(&p->rlock); + if(!interp) + p->killed = 1; + r = p->r; + if(r != nil) { + lock(&r->l); + if(r->p == p){ + p->swipend = 1; + r->p = nil; + osready(p); + } + unlock(&r->l); + unlock(&p->rlock); + return; + } + unlock(&p->rlock); + + /* + * Maybe pull out of Host OS + */ + lock(&p->sysio); + if(p->syscall && p->intwait == 0) { + p->swipend = 1; + p->intwait = 1; + unlock(&p->sysio); + oshostintr(p); + return; + } + unlock(&p->sysio); +} + +void +notkilled(void) +{ + lock(&up->rlock); + up->killed = 0; + unlock(&up->rlock); +} + +void +osenter(void) +{ + up->syscall = 1; +} + +void +osleave(void) +{ + int r; + + lock(&up->rlock); + r = up->swipend; + up->swipend = 0; + unlock(&up->rlock); + + lock(&up->sysio); + up->syscall = 0; + unlock(&up->sysio); + + /* Cleared by the signal/note/exception handler */ + while(up->intwait) + osyield(); + + if(r != 0) + error(Eintr); +} + +void +rptwakeup(void *o, void *ar) +{ + Rept *r; + + r = ar; + if(r == nil) + return; + lock(&r->l); + r->o = o; + unlock(&r->l); + Wakeup(&r->r); +} + +static int +rptactive(void *a) +{ + Rept *r = a; + int i; + lock(&r->l); + i = r->active(r->o); + unlock(&r->l); + return i; +} + +static void +rproc(void *a) +{ + long now, then; + ulong t; + int i; + void *o; + Rept *r; + + up->env->fgrp = newfgrp(nil); + r = a; + t = (ulong)r->t; + +Wait: + Sleep(&r->r, rptactive, r); + lock(&r->l); + o = r->o; + unlock(&r->l); + then = osmillisec(); + for(;;){ + osmillisleep(t); + now = osmillisec(); + if(waserror()) + break; + i = r->ck(o, now-then); + poperror(); + if(i == -1) + goto Wait; + if(i == 0) + continue; + then = now; + acquire(); + if(waserror()) { + release(); + break; + } + r->f(o); + poperror(); + release(); + } + pexit("", 0); +} + +void* +rptproc(char *s, int t, void *o, int (*active)(void*), int (*ck)(void*, int), void (*f)(void*)) +{ + Rept *r; + + r = mallocz(sizeof(Rept), 1); + if(r == nil) + return nil; + r->t = t; + r->active = active; + r->ck = ck; + r->f = f; + r->o = o; + kproc(s, rproc, r, KPDUPPG); + return r; +} diff --git a/emu/port/qio.c b/emu/port/qio.c new file mode 100644 index 00000000..1dfa5d32 --- /dev/null +++ b/emu/port/qio.c @@ -0,0 +1,1531 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +#define QDEBUG if(0) + +/* + * IO queues + */ + +struct Queue +{ + Lock l; + + 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* 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]; + int want; +}; + +enum +{ + Maxatomic = 32*1024 +}; + +uint qiomaxatomic = Maxatomic; + +void +checkb(Block *b, char *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 +freeb(Block *b) +{ + 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; + } + + /* poison the block in case someone is still holding onto it */ + b->next = (void*)0xdeadbabe; + b->rp = (void*)0xdeadbabe; + b->wp = (void*)0xdeadbabe; + b->lim = (void*)0xdeadbabe; + b->base = (void*)0xdeadbabe; + + free(b); +} + +/* + * free a list of blocks + */ +void +freeblist(Block *b) +{ + Block *next; + + for(; b != 0; b = next){ + next = b->next; + b->next = 0; + freeb(b); + } +} + +ulong padblockoverhead; + +/* + * pad a block to the front (or the back if size is negative) + */ +Block* +padblock(Block *bp, int size) +{ + int n; + Block *nbp; + + if(size >= 0){ + if(bp->rp - bp->base >= size){ + bp->rp -= size; + return bp; + } + + if(bp->next) + panic("padblock 0x%luX", getcallerpc(&bp)); + n = BLEN(bp); + padblockoverhead += n; + 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%luX", getcallerpc(&bp)); + + if(bp->lim - bp->wp >= size) + return bp; + + n = BLEN(bp); + padblockoverhead += n; + nbp = allocb(size+n); + memmove(nbp->wp, bp->rp, n); + nbp->wp += n; + freeb(bp); + } + 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; + } + freeblist(bp); + return nb; +} + +/* + * make sure the first block has at least n bytes. If we started with + * less than n bytes, make sure we have exactly n bytes. devssl.c depends + * on this. + */ +Block* +pullupblock(Block *bp, int n) +{ + int i; + Block *nbp; + + /* + * this should almost always be true, the rest it + * 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); + bp->wp += n; + nbp->rp += n; + return bp; + } + else { + memmove(bp->wp, nbp->rp, i); + bp->wp += i; + bp->next = nbp->next; + nbp->next = 0; + freeb(nbp); + n -= i; + if(n == 0) + return bp; + } + } + freeblist(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; + + 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; + + 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; + } + + 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; + if(BLEN(bp) == 0) { + *bph = bp->next; + bp->next = nil; + freeb(bp); + } + } + return bytes; +} + +/* + * allocate queues and blocks (round data base address to 64 bit boundary) + */ +Block* +iallocb(int size) +{ + Block *b; + ulong addr; + + b = kmalloc(sizeof(Block)+size); + if(b == 0) + return 0; + memset(b, 0, sizeof(Block)); + + addr = (ulong)b + sizeof(Block); + b->base = (uchar*)addr; + b->lim = b->base + size; + b->rp = b->base; + b->wp = b->rp; + + return b; +} + + +/* + * call error if iallocb fails + */ +Block* +allocb(int size) +{ + Block *b; + + b = iallocb(size); + if(b == 0) + exhausted("allocb"); + return b; +} + + +/* + * get next block from a queue, return null if nothing there + */ +Block* +qget(Queue *q) +{ + int dowakeup; + Block *b; + + /* sync with qwrite */ + lock(&q->l); + + b = q->bfirst; + if(b == 0){ + q->state |= Qstarve; + unlock(&q->l); + return 0; + } + 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; + + unlock(&q->l); + + 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; + + lock(&q->l); + 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 */ + if((q->state & Qflow) && q->len < q->limit){ + q->state &= ~Qflow; + dowakeup = 1; + } else + dowakeup = 0; + + unlock(&q->l); + + 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 */ + lock(&q->l); + + for(;;) { + b = q->bfirst; + if(b == 0){ + q->state |= Qstarve; + unlock(&q->l); + 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); + if((q->state & Qmsg) || len == n) + q->bfirst = b->next; + b->rp += len; + q->dlen -= len; + + /* discard the block if we're done with it */ + if((q->state & Qmsg) || len == n){ + 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; + + unlock(&q->l); + + 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; + lock(&q->l); + if(q->len >= q->limit){ + unlock(&q->l); + freeblist(b); + return -1; + } + if(q->state & Qclosed){ + unlock(&q->l); + len = blocklen(b); + freeblist(b); + return len; + } + + /* 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; + } + unlock(&q->l); + + if(dowakeup) + Wakeup(&q->rr); + + return len; +} + +int +qpassnolim(Queue *q, Block *b) +{ + int dlen, len, dowakeup; + + /* sync with qread */ + dowakeup = 0; + lock(&q->l); + + len = BALLOC(b); + if(q->state & Qclosed){ + unlock(&q->l); + freeblist(b); + return len; + } + + /* add buffer to queue */ + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = 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; + } + unlock(&q->l); + + 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; + lock(&q->l); + + if(q->state & Qclosed){ + unlock(&q->l); + return -1; + } + + /* no waiting receivers, room in buffer? */ + if(q->len >= q->limit){ + q->state |= Qflow; + unlock(&q->l); + return -1; + } + + /* save in buffer */ + b = iallocb(len); + if(b == 0){ + unlock(&q->l); + print("qproduce: iallocb failed\n"); + return -1; + } + memmove(b->wp, p, len); + b->wp += len; + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + q->blast = b; + /* b->next = 0; done by allocb() */ + 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; + unlock(&q->l); + + + 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); + + lock(&q->l); + + /* go to offset */ + b = q->bfirst; + for(sofar = 0; ; sofar += n){ + if(b == nil){ + unlock(&q->l); + return nb; + } + n = BLEN(b); + if(sofar + n > offset){ + p = b->rp + offset - sofar; + n -= offset - sofar; + break; + } + b = b->next; + } + + /* copy bytes from there */ + for(sofar = 0; sofar < len;){ + if(n > len - sofar) + n = len - sofar; + memmove(nb->wp, p, n); + sofar += n; + nb->wp += n; + b = b->next; + if(b == nil) + break; + n = BLEN(b); + p = b->rp; + } + unlock(&q->l); + + return nb; +} + +/* + * called by non-interrupt code + */ +Queue* +qopen(int limit, int msg, void (*kick)(void*), void *arg) +{ + Queue *q; + + q = kmalloc(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; +} + +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 */ + unlock(&q->l); + Sleep(&q->rr, notempty, q); + lock(&q->l); + } + 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 locked + */ +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 locked + */ +static void +qwakeup_unlock(Queue *q) +{ + int dowakeup = 0; + + /* if writer flow controlled, restart */ + if((q->state & Qflow) && q->len < q->limit/2){ + q->state &= ~Qflow; + dowakeup = 1; + } + + unlock(&q->l); + + /* 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(); + } + + lock(&q->l); + switch(qwait(q)){ + case 0: + /* queue closed */ + unlock(&q->l); + poperror(); + qunlock(&q->rlock); + return nil; + case -1: + /* multiple reads on a closed queue */ + unlock(&q->l); + 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 oriented 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_unlock(q); + + poperror(); + qunlock(&q->rlock); + return nb; +} + +/* + * read a queue. if no data is queued, 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(); + } + + lock(&q->l); +again: + switch(qwait(q)){ + case 0: + /* queue closed */ + unlock(&q->l); + poperror(); + qunlock(&q->rlock); + return 0; + case -1: + /* multiple reads on a closed queue */ + unlock(&q->l); + 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 */ + unlock(&q->l); + b = bl2mem(vp, first, len); + lock(&q->l); + + /* 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_unlock(q); + + poperror(); + qunlock(&q->rlock); + return n; +} + +static int +qnotfull(void *a) +{ + Queue *q = a; + + return q->len < q->limit || (q->state & Qclosed); +} + +/* + * add a block to a queue obeying flow control + */ +long +qbwrite(Queue *q, Block *b) +{ + int n, dowakeup; + volatile struct {Block *b;} cb; + + dowakeup = 0; + n = BLEN(b); + cb.b = b; + + qlock(&q->wlock); + if(waserror()){ + if(cb.b != nil) + freeb(cb.b); + qunlock(&q->wlock); + nexterror(); + } + + lock(&q->l); + + /* give up if the queue is closed */ + if(q->state & Qclosed){ + unlock(&q->l); + error(q->err); + } + + /* if nonblocking, don't queue over the limit */ + if(q->len >= q->limit){ + if(q->noblock){ + unlock(&q->l); + freeb(b); + poperror(); + qunlock(&q->wlock); + 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"); + cb.b = nil; + + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + + unlock(&q->l); + + /* get output going again */ + if(q->kick && (dowakeup || (q->state&Qkick))) + q->kick(q->arg); + + if(dowakeup) + Wakeup(&q->rr); + + /* + * 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; + + lock(&q->l); + q->state |= Qflow; + unlock(&q->l); + Sleep(&q->wr, qnotfull, q); + } + + 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; + + sofar = 0; + do { + n = len-sofar; + if(n > Maxatomic) + n = Maxatomic; + + b = allocb(n); + setmalloctag(b, getcallerpc(&q)); + 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 == 0) { + print("qiwrite: iallocb failed\n"); + break; + } + memmove(b->wp, p+sofar, n); + b->wp += n; + + lock(&q->l); + + 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; + } + + unlock(&q->l); + + 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 */ + lock(&q->l); + 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; + unlock(&q->l); + + /* 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 */ + lock(&q->l); + q->state |= Qclosed; + if(msg == 0 || *msg == 0) + strcpy(q->err, Ehungup); + else + kstrcpy(q->err, msg, sizeof q->err); + unlock(&q->l); + + /* 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) +{ + lock(&q->l); + q->state &= ~Qclosed; + q->state |= Qstarve; + q->eof = 0; + q->limit = q->inilim; + unlock(&q->l); +} + +/* + * 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 */ + lock(&q->l); + bfirst = q->bfirst; + q->bfirst = 0; + q->len = 0; + q->dlen = 0; + unlock(&q->l); + + /* 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; +} + +int +qclosed(Queue *q) +{ + return q->state & Qclosed; +} diff --git a/emu/port/random.c b/emu/port/random.c new file mode 100644 index 00000000..05ae2b9a --- /dev/null +++ b/emu/port/random.c @@ -0,0 +1,167 @@ +#include "dat.h" +#include "fns.h" + +static struct +{ + QLock l; + Rendez producer; + Rendez consumer; + Rendez clock; + ulong randomcount; + uchar buf[1024]; + uchar *ep; + uchar *rp; + uchar *wp; + uchar next; + uchar bits; + uchar wakeme; + uchar filled; + int kprocstarted; + ulong randn; + int target; +} rb; + +static int +rbnotfull(void *v) +{ + int i; + + USED(v); + i = rb.wp - rb.rp; + if(i < 0) + i += sizeof(rb.buf); + return i < rb.target; +} + +static int +rbnotempty(void *v) +{ + USED(v); + return rb.wp != rb.rp; +} + +/* + * spin counting up + */ +static void +genrandom(void *v) +{ + USED(v); + oslopri(); + for(;;){ + for(;;) + if(++rb.randomcount > 65535) + break; + if(!rbnotfull(0)) + Sleep(&rb.producer, rbnotfull, 0); + } +} + +/* + * produce random bits in a circular buffer + */ +static void +randomclock(void *v) +{ + uchar *p; + + USED(v); + for(;; osmillisleep(20)){ + while(!rbnotfull(0)){ + rb.filled = 1; + Sleep(&rb.clock, rbnotfull, 0); + } + if(rb.randomcount == 0) + continue; + + rb.bits = (rb.bits<<2) ^ rb.randomcount; + rb.randomcount = 0; + + rb.next++; + if(rb.next != 8/2) + continue; + rb.next = 0; + + p = rb.wp; + *p ^= rb.bits; + if(++p == rb.ep) + p = rb.buf; + rb.wp = p; + + if(rb.wakeme) + Wakeup(&rb.consumer); + } +} + +void +randominit(void) +{ + rb.target = 16; + rb.ep = rb.buf + sizeof(rb.buf); + rb.rp = rb.wp = rb.buf; +} + +/* + * consume random bytes from a circular buffer + */ +ulong +randomread(void *xp, ulong n) +{ + uchar *e, *p, *r; + ulong x; + int i; + + p = xp; + +if(0)print("A%ld.%d.%lux|", n, rb.target, getcallerpc(&xp)); + if(waserror()){ + qunlock(&rb.l); + nexterror(); + } + + qlock(&rb.l); + if(!rb.kprocstarted){ + rb.kprocstarted = 1; + kproc("genrand", genrandom, 0, 0); + kproc("randomclock", randomclock, 0, 0); + } + + for(e = p + n; p < e; ){ + r = rb.rp; + if(r == rb.wp){ + rb.wakeme = 1; + Wakeup(&rb.clock); + Wakeup(&rb.producer); + Sleep(&rb.consumer, rbnotempty, 0); + rb.wakeme = 0; + continue; + } + + /* + * beating clocks will be predictable if + * they are synchronized. Use a cheap pseudo + * random number generator to obscure any cycles. + */ + x = rb.randn*1103515245 ^ *r; + *p++ = rb.randn = x; + + if(++r == rb.ep) + r = rb.buf; + rb.rp = r; + } + if(rb.filled && rb.wp == rb.rp){ + i = 2*rb.target; + if(i > sizeof(rb.buf) - 1) + i = sizeof(rb.buf) - 1; + rb.target = i; + rb.filled = 0; + } + qunlock(&rb.l); + poperror(); + + Wakeup(&rb.clock); + Wakeup(&rb.producer); + +if(0)print("B"); + return n; +} diff --git a/emu/port/srv.c b/emu/port/srv.c new file mode 100644 index 00000000..86a004c8 --- /dev/null +++ b/emu/port/srv.c @@ -0,0 +1,153 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include <interp.h> +#include <isa.h> +#include "ip.h" +#include "srv.h" +#include "srvm.h" + +static QLock dbq; + +void +Srv_init(void *fp) +{ + USED(fp); +} + +void +Srv_iph2a(void *fp) +{ + Heap *hpt; + String *ss; + F_Srv_iph2a *f; + int i, n, nhost; + List **h, *l, *nl; + char *hostv[10]; + void *r; + + f = fp; + r = *f->ret; + *f->ret = H; + destroy(r); + release(); + qlock(&dbq); + if(waserror()){ + qunlock(&dbq); + acquire(); + return; + } + nhost = so_gethostbyname(string2c(f->host), hostv, nelem(hostv)); + poperror(); + qunlock(&dbq); + acquire(); + if(nhost == 0) + return; + + l = (List*)H; + h = &l; + for(i = 0; i < nhost; i++) { + n = strlen(hostv[i]); + ss = newstring(n); + memmove(ss->Sascii, hostv[i], n); + free(hostv[i]); + + hpt = nheap(sizeof(List) + IBY2WD); + hpt->t = &Tlist; + hpt->t->ref++; + nl = H2D(List*, hpt); + nl->t = &Tptr; + Tptr.ref++; + nl->tail = (List*)H; + *(String**)nl->data = ss; + + *h = nl; + h = &nl->tail; + } + *f->ret = l; +} + +void +Srv_ipa2h(void *fp) +{ + Heap *hpt; + String *ss; + F_Srv_ipa2h *f; + int i, n, naliases; + List **h, *l, *nl; + char *hostv[10]; + void *r; + + f = fp; + r = *f->ret; + *f->ret = H; + destroy(r); + release(); + qlock(&dbq); + if(waserror()){ + qunlock(&dbq); + acquire(); + return; + } + naliases = so_gethostbyaddr(string2c(f->addr), hostv, nelem(hostv)); + poperror(); + qunlock(&dbq); + acquire(); + if(naliases == 0) + return; + + l = (List*)H; + h = &l; + for(i = 0; i < naliases; i++) { + n = strlen(hostv[i]); + ss = newstring(n); + memmove(ss->Sascii, hostv[i], n); + free(hostv[i]); + + hpt = nheap(sizeof(List) + IBY2WD); + hpt->t = &Tlist; + hpt->t->ref++; + nl = H2D(List*, hpt); + nl->t = &Tptr; + Tptr.ref++; + nl->tail = (List*)H; + *(String**)nl->data = ss; + + *h = nl; + h = &nl->tail; + } + *f->ret = l; +} + +void +Srv_ipn2p(void *fp) +{ + int n; + char buf[16]; + F_Srv_ipn2p *f; + void *r; + + f = fp; + r = *f->ret; + *f->ret = H; + destroy(r); + release(); + qlock(&dbq); + if(waserror()){ + qunlock(&dbq); + acquire(); + return; + } + n = so_getservbyname(string2c(f->service), string2c(f->net), buf); + poperror(); + qunlock(&dbq); + acquire(); + if(n >= 0) + retstr(buf, f->ret); +} + +void +srvmodinit(void) +{ + builtinmod("$Srv", Srvmodtab, Srvmodlen); +} diff --git a/emu/port/styx.c b/emu/port/styx.c new file mode 100644 index 00000000..2eb9d757 --- /dev/null +++ b/emu/port/styx.c @@ -0,0 +1,701 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +static +uchar* +gstring(uchar *p, uchar *ep, char **s) +{ + uint n; + + if(p+BIT16SZ > ep) + return nil; + n = GBIT16(p); + p += BIT16SZ - 1; + if(p+n+1 > ep) + return nil; + /* move it down, on top of count, to make room for '\0' */ + memmove(p, p + 1, n); + p[n] = '\0'; + *s = (char*)p; + p += n+1; + return p; +} + +static +uchar* +gqid(uchar *p, uchar *ep, Qid *q) +{ + if(p+QIDSZ > ep) + return nil; + q->type = GBIT8(p); + p += BIT8SZ; + q->vers = GBIT32(p); + p += BIT32SZ; + q->path = GBIT64(p); + p += BIT64SZ; + return p; +} + +/* + * no syntactic checks. + * three causes for error: + * 1. message size field is incorrect + * 2. input buffer too short for its own data (counts too long, etc.) + * 3. too many names or qids + * gqid() and gstring() return nil if they would reach beyond buffer. + * main switch statement checks range and also can fall through + * to test at end of routine. + */ +uint +convM2S(uchar *ap, uint nap, Fcall *f) +{ + uchar *p, *ep; + uint i, size; + + p = ap; + ep = p + nap; + + if(p+BIT32SZ+BIT8SZ+BIT16SZ > ep) + return 0; + size = GBIT32(p); + p += BIT32SZ; + + if(size < BIT32SZ+BIT8SZ+BIT16SZ) + return 0; + + f->type = GBIT8(p); + p += BIT8SZ; + f->tag = GBIT16(p); + p += BIT16SZ; + + switch(f->type) + { + default: + return 0; + + case Tversion: + if(p+BIT32SZ > ep) + return 0; + f->msize = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->version); + break; + + case Tflush: + if(p+BIT16SZ > ep) + return 0; + f->oldtag = GBIT16(p); + p += BIT16SZ; + break; + + case Tauth: + if(p+BIT32SZ > ep) + return 0; + f->afid = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->uname); + if(p == nil) + break; + p = gstring(p, ep, &f->aname); + if(p == nil) + break; + break; + + case Tattach: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + if(p+BIT32SZ > ep) + return 0; + f->afid = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->uname); + if(p == nil) + break; + p = gstring(p, ep, &f->aname); + if(p == nil) + break; + break; + + case Twalk: + if(p+BIT32SZ+BIT32SZ+BIT16SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->newfid = GBIT32(p); + p += BIT32SZ; + f->nwname = GBIT16(p); + p += BIT16SZ; + if(f->nwname > MAXWELEM) + return 0; + for(i=0; i<f->nwname; i++){ + p = gstring(p, ep, &f->wname[i]); + if(p == nil) + break; + } + break; + + case Topen: + if(p+BIT32SZ+BIT8SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->mode = GBIT8(p); + p += BIT8SZ; + break; + + case Tcreate: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->name); + if(p == nil) + break; + if(p+BIT32SZ+BIT8SZ > ep) + return 0; + f->perm = GBIT32(p); + p += BIT32SZ; + f->mode = GBIT8(p); + p += BIT8SZ; + break; + + case Tread: + if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->offset = GBIT64(p); + p += BIT64SZ; + f->count = GBIT32(p); + p += BIT32SZ; + break; + + case Twrite: + if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->offset = GBIT64(p); + p += BIT64SZ; + f->count = GBIT32(p); + p += BIT32SZ; + if(p+f->count > ep) + return 0; + f->data = (char*)p; + p += f->count; + break; + + case Tclunk: + case Tremove: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + break; + + case Tstat: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + break; + + case Twstat: + if(p+BIT32SZ+BIT16SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->nstat = GBIT16(p); + p += BIT16SZ; + if(p+f->nstat > ep) + return 0; + f->stat = p; + p += f->nstat; + break; + +/* + */ + case Rversion: + if(p+BIT32SZ > ep) + return 0; + f->msize = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->version); + break; + + case Rerror: + p = gstring(p, ep, &f->ename); + break; + + case Rflush: + break; + + case Rauth: + p = gqid(p, ep, &f->aqid); + if(p == nil) + break; + break; + + case Rattach: + p = gqid(p, ep, &f->qid); + if(p == nil) + break; + break; + + case Rwalk: + if(p+BIT16SZ > ep) + return 0; + f->nwqid = GBIT16(p); + p += BIT16SZ; + if(f->nwqid > MAXWELEM) + return 0; + for(i=0; i<f->nwqid; i++){ + p = gqid(p, ep, &f->wqid[i]); + if(p == nil) + break; + } + break; + + case Ropen: + case Rcreate: + p = gqid(p, ep, &f->qid); + if(p == nil) + break; + if(p+BIT32SZ > ep) + return 0; + f->iounit = GBIT32(p); + p += BIT32SZ; + break; + + case Rread: + if(p+BIT32SZ > ep) + return 0; + f->count = GBIT32(p); + p += BIT32SZ; + if(p+f->count > ep) + return 0; + f->data = (char*)p; + p += f->count; + break; + + case Rwrite: + if(p+BIT32SZ > ep) + return 0; + f->count = GBIT32(p); + p += BIT32SZ; + break; + + case Rclunk: + case Rremove: + break; + + case Rstat: + if(p+BIT16SZ > ep) + return 0; + f->nstat = GBIT16(p); + p += BIT16SZ; + if(p+f->nstat > ep) + return 0; + f->stat = p; + p += f->nstat; + break; + + case Rwstat: + break; + } + + if(p==nil || p>ep) + return 0; + if(ap+size == p) + return size; + return 0; +} + + + + +static +uchar* +pstring(uchar *p, char *s) +{ + uint n; + + if(s == nil){ + PBIT16(p, 0); + p += BIT16SZ; + return p; + } + + n = strlen(s); + PBIT16(p, n); + p += BIT16SZ; + memmove(p, s, n); + p += n; + return p; +} + +static +uchar* +pqid(uchar *p, Qid *q) +{ + PBIT8(p, q->type); + p += BIT8SZ; + PBIT32(p, q->vers); + p += BIT32SZ; + PBIT64(p, q->path); + p += BIT64SZ; + return p; +} + +static +uint +stringsz(char *s) +{ + if(s == nil) + return BIT16SZ; + + return BIT16SZ+strlen(s); +} + +uint +sizeS2M(Fcall *f) +{ + uint n; + int i; + + n = 0; + n += BIT32SZ; /* size */ + n += BIT8SZ; /* type */ + n += BIT16SZ; /* tag */ + + switch(f->type) + { + default: + return 0; + + case Tversion: + n += BIT32SZ; + n += stringsz(f->version); + break; + + case Tflush: + n += BIT16SZ; + break; + + case Tauth: + n += BIT32SZ; + n += stringsz(f->uname); + n += stringsz(f->aname); + break; + + case Tattach: + n += BIT32SZ; + n += BIT32SZ; + n += stringsz(f->uname); + n += stringsz(f->aname); + break; + + case Twalk: + n += BIT32SZ; + n += BIT32SZ; + n += BIT16SZ; + for(i=0; i<f->nwname; i++) + n += stringsz(f->wname[i]); + break; + + case Topen: + n += BIT32SZ; + n += BIT8SZ; + break; + + case Tcreate: + n += BIT32SZ; + n += stringsz(f->name); + n += BIT32SZ; + n += BIT8SZ; + break; + + case Tread: + n += BIT32SZ; + n += BIT64SZ; + n += BIT32SZ; + break; + + case Twrite: + n += BIT32SZ; + n += BIT64SZ; + n += BIT32SZ; + n += f->count; + break; + + case Tclunk: + case Tremove: + n += BIT32SZ; + break; + + case Tstat: + n += BIT32SZ; + break; + + case Twstat: + n += BIT32SZ; + n += BIT16SZ; + n += f->nstat; + break; +/* + */ + + case Rversion: + n += BIT32SZ; + n += stringsz(f->version); + break; + + case Rerror: + n += stringsz(f->ename); + break; + + case Rflush: + break; + + case Rauth: + n += QIDSZ; + break; + + case Rattach: + n += QIDSZ; + break; + + case Rwalk: + n += BIT16SZ; + n += f->nwqid*QIDSZ; + break; + + case Ropen: + case Rcreate: + n += QIDSZ; + n += BIT32SZ; + break; + + case Rread: + n += BIT32SZ; + n += f->count; + break; + + case Rwrite: + n += BIT32SZ; + break; + + case Rclunk: + break; + + case Rremove: + break; + + case Rstat: + n += BIT16SZ; + n += f->nstat; + break; + + case Rwstat: + break; + } + return n; +} + +uint +convS2M(Fcall *f, uchar *ap, uint nap) +{ + uchar *p; + uint i, size; + + size = sizeS2M(f); + if(size == 0) + return 0; + if(size > nap) + return 0; + + p = (uchar*)ap; + + PBIT32(p, size); + p += BIT32SZ; + PBIT8(p, f->type); + p += BIT8SZ; + PBIT16(p, f->tag); + p += BIT16SZ; + + switch(f->type) + { + default: + return 0; + + case Tversion: + PBIT32(p, f->msize); + p += BIT32SZ; + p = pstring(p, f->version); + break; + + case Tflush: + PBIT16(p, f->oldtag); + p += BIT16SZ; + break; + + case Tauth: + PBIT32(p, f->afid); + p += BIT32SZ; + p = pstring(p, f->uname); + p = pstring(p, f->aname); + break; + + case Tattach: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT32(p, f->afid); + p += BIT32SZ; + p = pstring(p, f->uname); + p = pstring(p, f->aname); + break; + + case Twalk: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT32(p, f->newfid); + p += BIT32SZ; + PBIT16(p, f->nwname); + p += BIT16SZ; + if(f->nwname > MAXWELEM) + return 0; + for(i=0; i<f->nwname; i++) + p = pstring(p, f->wname[i]); + break; + + case Topen: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT8(p, f->mode); + p += BIT8SZ; + break; + + case Tcreate: + PBIT32(p, f->fid); + p += BIT32SZ; + p = pstring(p, f->name); + PBIT32(p, f->perm); + p += BIT32SZ; + PBIT8(p, f->mode); + p += BIT8SZ; + break; + + case Tread: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT64(p, f->offset); + p += BIT64SZ; + PBIT32(p, f->count); + p += BIT32SZ; + break; + + case Twrite: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT64(p, f->offset); + p += BIT64SZ; + PBIT32(p, f->count); + p += BIT32SZ; + memmove(p, f->data, f->count); + p += f->count; + break; + + case Tclunk: + case Tremove: + PBIT32(p, f->fid); + p += BIT32SZ; + break; + + case Tstat: + PBIT32(p, f->fid); + p += BIT32SZ; + break; + + case Twstat: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT16(p, f->nstat); + p += BIT16SZ; + memmove(p, f->stat, f->nstat); + p += f->nstat; + break; +/* + */ + + case Rversion: + PBIT32(p, f->msize); + p += BIT32SZ; + p = pstring(p, f->version); + break; + + case Rerror: + p = pstring(p, f->ename); + break; + + case Rflush: + break; + + case Rauth: + p = pqid(p, &f->aqid); + break; + + case Rattach: + p = pqid(p, &f->qid); + break; + + case Rwalk: + PBIT16(p, f->nwqid); + p += BIT16SZ; + if(f->nwqid > MAXWELEM) + return 0; + for(i=0; i<f->nwqid; i++) + p = pqid(p, &f->wqid[i]); + break; + + case Ropen: + case Rcreate: + p = pqid(p, &f->qid); + PBIT32(p, f->iounit); + p += BIT32SZ; + break; + + case Rread: + PBIT32(p, f->count); + p += BIT32SZ; + memmove(p, f->data, f->count); + p += f->count; + break; + + case Rwrite: + PBIT32(p, f->count); + p += BIT32SZ; + break; + + case Rclunk: + break; + + case Rremove: + break; + + case Rstat: + PBIT16(p, f->nstat); + p += BIT16SZ; + memmove(p, f->stat, f->nstat); + p += f->nstat; + break; + + case Rwstat: + break; + } + if(size != p-ap) + return 0; + return size; +} diff --git a/emu/port/sysfile.c b/emu/port/sysfile.c new file mode 100644 index 00000000..db7a06eb --- /dev/null +++ b/emu/port/sysfile.c @@ -0,0 +1,1089 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "kernel.h" + +static int +growfd(Fgrp *f, int fd) +{ + int n; + Chan **nfd, **ofd; + + if(fd < f->nfd) + return 0; + n = f->nfd+DELTAFD; + if(n > MAXNFD) + n = MAXNFD; + if(fd >= n) + return -1; + nfd = malloc(n*sizeof(Chan*)); + if(nfd == nil) + return -1; + ofd = f->fd; + memmove(nfd, ofd, f->nfd*sizeof(Chan *)); + f->fd = nfd; + f->nfd = n; + free(ofd); + return 0; +} + +static int +newfd(Chan *c) +{ + int i; + Fgrp *f = up->env->fgrp; + + lock(&f->l); + for(i=f->minfd; i<f->nfd; i++) + if(f->fd[i] == 0) + break; + if(i >= f->nfd && growfd(f, i) < 0){ + unlock(&f->l); + exhausted("file descriptors"); + return -1; + } + f->minfd = i + 1; + if(i > f->maxfd) + f->maxfd = i; + f->fd[i] = c; + unlock(&f->l); + return i; +} + +Chan* +fdtochan(Fgrp *f, int fd, int mode, int chkmnt, int iref) +{ + Chan *c; + + c = 0; + lock(&f->l); + if(fd<0 || f->maxfd<fd || (c = f->fd[fd])==0) { + unlock(&f->l); + error(Ebadfd); + } + if(iref) + incref(&c->r); + unlock(&f->l); + + 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; +} + +long +kchanio(void *vc, void *buf, int n, int mode) +{ + int r; + Chan *c; + + c = vc; + if(waserror()) + return -1; + + if(mode == OREAD) + r = devtab[c->type]->read(c, buf, n, c->offset); + else + r = devtab[c->type]->write(c, buf, n, c->offset); + + lock(&c->l); + c->offset += r; + unlock(&c->l); + poperror(); + return r; +} + +int +openmode(ulong o) +{ + if(o >= (OTRUNC|OCEXEC|ORCLOSE|OEXEC)) + error(Ebadarg); + o &= ~(OTRUNC|OCEXEC|ORCLOSE); + if(o > OEXEC) + error(Ebadarg); + if(o == OEXEC) + return OREAD; + return o; +} + +static void +fdclose(Fgrp *f, int fd) +{ + int i; + Chan *c; + + lock(&f->l); + c = f->fd[fd]; + if(c == 0) { + /* can happen for users with shared fd tables */ + unlock(&f->l); + return; + } + f->fd[fd] = 0; + if(fd == f->maxfd) + for(i=fd; --i>=0 && f->fd[i]==0; ) + f->maxfd = i; + if(fd < f->minfd) + f->minfd = fd; + unlock(&f->l); + cclose(c); +} + +int +kchdir(char *path) +{ + Chan *c; + Pgrp *pg; + + if(waserror()) + return -1; + + c = namec(path, Atodir, 0, 0); + pg = up->env->pgrp; + cclose(pg->dot); + pg->dot = c; + poperror(); + return 0; +} + +int +kfgrpclose(Fgrp *f, int fd) +{ + if(waserror()) + return -1; + + /* + * Take no reference on the chan because we don't really need the + * data structure, and are calling fdtochan only for error checks. + * fdclose takes care of processes racing through here. + */ + fdtochan(f, fd, -1, 0, 0); + fdclose(f, fd); + poperror(); + return 0; +} + +int +kclose(int fd) +{ + return kfgrpclose(up->env->fgrp, fd); +} + +int +kcreate(char *path, int mode, ulong perm) +{ + int fd; + volatile struct { Chan *c; } c; + + c.c = nil; + if(waserror()) { + cclose(c.c); + return -1; + } + + openmode(mode&~OEXCL); /* error check only; OEXCL okay here */ + c.c = namec(path, Acreate, mode, perm); + fd = newfd(c.c); + if(fd < 0) + error(Enofd); + poperror(); + return fd; +} + +int +kdup(int old, int new) +{ + int fd; + Chan *oc; + Fgrp *f = up->env->fgrp; + volatile struct { Chan *c; } c; + + if(waserror()) + return -1; + + c.c = fdtochan(up->env->fgrp, old, -1, 0, 1); + if(c.c->qid.type & QTAUTH) + error(Eperm); + fd = new; + if(fd != -1) { + lock(&f->l); + if(fd < 0 || growfd(f, fd) < 0) { + unlock(&f->l); + cclose(c.c); + error(Ebadfd); + } + if(fd > f->maxfd) + f->maxfd = fd; + oc = f->fd[fd]; + f->fd[fd] = c.c; + unlock(&f->l); + if(oc != 0) + cclose(oc); + } + else { + if(waserror()) { + cclose(c.c); + nexterror(); + } + fd = newfd(c.c); + if(fd < 0) + error(Enofd); + poperror(); + } + poperror(); + return fd; +} + +int +kfstat(int fd, uchar *buf, int n) +{ + volatile struct { Chan *c; } c; + + c.c = nil; + if(waserror()) { + cclose(c.c); + return -1; + } + c.c = fdtochan(up->env->fgrp, fd, -1, 0, 1); + devtab[c.c->type]->stat(c.c, buf, n); + poperror(); + cclose(c.c); + return n; +} + +char* +kfd2path(int fd) +{ + Chan *c; + char *s; + + if(waserror()) + return nil; + c = fdtochan(up->env->fgrp, fd, -1, 0, 1); + s = nil; + if(c->name != nil){ + s = malloc(c->name->len+1); + if(s == nil){ + cclose(c); + error(Enomem); + } + memmove(s, c->name->s, c->name->len+1); + cclose(c); + } + poperror(); + return s; +} + +int +kfauth(int fd, char *aname) +{ + Chan *c, *ac; + + if(waserror()) + return -1; + + validname(aname, 0); + c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1); + if(waserror()){ + cclose(c); + nexterror(); + } + + ac = mntauth(c, aname); + + /* at this point ac is responsible for keeping c alive */ + poperror(); /* c */ + cclose(c); + + if(waserror()){ + cclose(ac); + nexterror(); + } + + fd = newfd(ac); + if(fd < 0) + error(Enofd); + poperror(); /* ac */ + + poperror(); + + return fd; +} + +int +kfversion(int fd, uint msize, char *vers, uint arglen) +{ + int m; + Chan *c; + + if(waserror()) + return -1; + + /* check there's a NUL in the version string */ + if(arglen==0 || memchr(vers, 0, arglen)==0) + error(Ebadarg); + + c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1); + if(waserror()){ + cclose(c); + nexterror(); + } + + m = mntversion(c, vers, msize, arglen); + + poperror(); + cclose(c); + + poperror(); + return m; +} + +int +kpipe(int fd[2]) +{ + Dev *d; + Fgrp *f; + Chan *c[2]; + static char *names[] = {"data", "data1"}; + + f = up->env->fgrp; + + d = devtab[devno('|', 0)]; + c[0] = namec("#|", Atodir, 0, 0); + c[1] = 0; + fd[0] = -1; + fd[1] = -1; + if(waserror()) { + if(c[0] != 0) + cclose(c[0]); + if(c[1] != 0) + cclose(c[1]); + if(fd[0] >= 0) + f->fd[fd[0]]=0; + if(fd[1] >= 0) + f->fd[fd[1]]=0; + return -1; + } + c[1] = cclone(c[0]); + if(walk(&c[0], &names[0], 1, 1, nil) < 0) + error(Egreg); + if(walk(&c[1], &names[1], 1, 1, nil) < 0) + error(Egreg); + c[0] = d->open(c[0], ORDWR); + c[1] = d->open(c[1], ORDWR); + fd[0] = newfd(c[0]); + if(fd[0] < 0) + error(Enofd); + fd[1] = newfd(c[1]); + if(fd[1] < 0) + error(Enofd); + poperror(); + return 0; +} + +int +kfwstat(int fd, uchar *buf, int n) +{ + volatile struct { Chan *c; } c; + + c.c = nil; + if(waserror()) { + cclose(c.c); + return -1; + } + validstat(buf, n); + c.c = fdtochan(up->env->fgrp, fd, -1, 1, 1); + n = devtab[c.c->type]->wstat(c.c, buf, n); + poperror(); + cclose(c.c); + return n; +} + +long +bindmount(Chan *c, char *old, int flag, char *spec) +{ + int ret; + volatile struct { Chan *c; } c1; + + if(flag>MMASK || (flag&MORDER) == (MBEFORE|MAFTER)) + error(Ebadarg); + + c1.c = namec(old, Amount, 0, 0); + if(waserror()){ + cclose(c1.c); + nexterror(); + } + ret = cmount(c, c1.c, flag, spec); + + poperror(); + cclose(c1.c); + return ret; +} + +int +kbind(char *new, char *old, int flags) +{ + long r; + volatile struct { Chan *c; } c0; + + c0.c = nil; + if(waserror()) { + cclose(c0.c); + return -1; + } + c0.c = namec(new, Abind, 0, 0); + r = bindmount(c0.c, old, flags, ""); + poperror(); + cclose(c0.c); + return r; +} + +int +kmount(int fd, int afd, char *old, int flags, char *spec) +{ + long r; + volatile struct { Chan *c; } c0; + volatile struct { Chan *c; } bc; + volatile struct { Chan *c; } ac; + Mntparam mntparam; + + ac.c = nil; + bc.c = nil; + c0.c = nil; + if(waserror()) { + cclose(ac.c); + cclose(bc.c); + cclose(c0.c); + return -1; + } + bc.c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1); + if(afd >= 0) + ac.c = fdtochan(up->env->fgrp, afd, ORDWR, 0, 1); + mntparam.chan = bc.c; + mntparam.authchan = ac.c; + mntparam.spec = spec; + mntparam.flags = flags; + c0.c = devtab[devno('M', 0)]->attach((char*)&mntparam); + + r = bindmount(c0.c, old, flags, spec); + poperror(); + cclose(ac.c); + cclose(bc.c); + cclose(c0.c); + + return r; +} + +int +kunmount(char *old, char *new) +{ + volatile struct { Chan *c; } cmount; + volatile struct { Chan *c; } cmounted; + + cmount.c = nil; + cmounted.c = nil; + if(waserror()) { + cclose(cmount.c); + cclose(cmounted.c); + return -1; + } + + cmount.c = namec(new, Amount, 0, 0); + if(old != nil && old[0] != '\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.c = namec(old, Aopen, OREAD, 0); + } + + cunmount(cmount.c, cmounted.c); + poperror(); + cclose(cmount.c); + cclose(cmounted.c); + return 0; +} + +int +kopen(char *path, int mode) +{ + int fd; + volatile struct { Chan *c; } c; + + if(waserror()) + return -1; + + openmode(mode); /* error check only */ + c.c = namec(path, Aopen, mode, 0); + if(waserror()){ + cclose(c.c); + nexterror(); + } + fd = newfd(c.c); + if(fd < 0) + error(Enofd); + poperror(); + + poperror(); + return fd; +} + +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); + if(nr < 0) + nr = 0; /* dev.c can return -1 */ + 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 void +unionrewind(Chan *c) +{ + qlock(&c->umqlock); + c->uri = 0; + if(c->umc){ + cclose(c->umc); + c->umc = nil; + } + qunlock(&c->umqlock); +} + +static long +rread(int fd, void *va, long n, vlong *offp) +{ + int dir; + Lock *cl; + volatile struct { Chan *c; } c; + vlong off; + + if(waserror()) + return -1; + + c.c = fdtochan(up->env->fgrp, fd, OREAD, 1, 1); + if(waserror()){ + cclose(c.c); + nexterror(); + } + + if(n < 0) + error(Etoosmall); + + dir = c.c->qid.type & QTDIR; + if(dir && c.c->umh) + n = unionread(c.c, va, n); + else{ + cl = &c.c->l; + if(offp == nil){ + lock(cl); /* lock for vlong assignment */ + off = c.c->offset; + unlock(cl); + }else + off = *offp; + if(off < 0) + error(Enegoff); + if(off == 0){ + if(offp == nil){ + lock(cl); + c.c->offset = 0; + c.c->dri = 0; + unlock(cl); + } + unionrewind(c.c); + } + n = devtab[c.c->type]->read(c.c, va, n, off); + lock(cl); + c.c->offset += n; + unlock(cl); + } + + poperror(); + cclose(c.c); + + poperror(); + return n; +} + +long +kread(int fd, void *va, long n) +{ + return rread(fd, va, n, nil); +} + +long +kpread(int fd, void *va, long n, vlong off) +{ + return rread(fd, va, n, &off); +} + +int +kremove(char *path) +{ + volatile struct { Chan *c; } c; + + if(waserror()) + return -1; + + c.c = namec(path, Aremove, 0, 0); + if(waserror()){ + c.c->type = 0; /* see below */ + cclose(c.c); + nexterror(); + } + devtab[c.c->type]->remove(c.c); + /* + * Remove clunks the fid, but we need to recover the Chan + * so fake it up. rootclose() is known to be a nop. + */ + c.c->type = 0; + poperror(); + cclose(c.c); + + poperror(); + return 0; +} + +vlong +kseek(int fd, vlong off, int whence) +{ + Dir *dir; + Chan *c; + + if(waserror()) + return -1; + + c = fdtochan(up->env->fgrp, 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){ + if(off != 0) + error(Eisdir); + unionrewind(c); + }else if(off < 0) + error(Enegoff); + lock(&c->l); /* lock for vlong assignment */ + c->offset = off; + unlock(&c->l); + break; + + case 1: + if(c->qid.type & QTDIR) + error(Eisdir); + lock(&c->l); /* lock for read/write update */ + off += c->offset; + if(off < 0){ + unlock(&c->l); + error(Enegoff); + } + c->offset = off; + unlock(&c->l); + break; + + case 2: + if(c->qid.type & QTDIR) + error(Eisdir); + dir = chandirstat(c); + if(dir == nil) + error("internal error: stat error in seek"); + off += dir->length; + free(dir); + if(off < 0) + error(Enegoff); + lock(&c->l); /* lock for read/write update */ + c->offset = off; + unlock(&c->l); + break; + + default: + error(Ebadarg); + break; + } + poperror(); + 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); +} + +int +kstat(char *path, uchar *buf, int n) +{ + volatile struct { Chan *c; } c; + + c.c = nil; + if(waserror()){ + cclose(c.c); + return -1; + } + c.c = namec(path, Aaccess, 0, 0); + devtab[c.c->type]->stat(c.c, buf, n); + poperror(); + cclose(c.c); + return 0; +} + +static long +rwrite(int fd, void *va, long n, vlong *offp) +{ + Lock *cl; + volatile struct { Chan *c; } c; + vlong off; + long m; + + if(waserror()) + return -1; + c.c = fdtochan(up->env->fgrp, fd, OWRITE, 1, 1); + if(waserror()){ + cclose(c.c); + nexterror(); + } + if(c.c->qid.type & QTDIR) + error(Eisdir); + + if(n < 0) + error(Etoosmall); + + cl = &c.c->l; + if(offp == nil){ + lock(cl); + off = c.c->offset; + c.c->offset += n; + unlock(cl); + }else + off = *offp; + + if(waserror()){ + if(offp == nil){ + lock(cl); + c.c->offset -= n; + unlock(cl); + } + nexterror(); + } + if(off < 0) + error(Enegoff); + m = devtab[c.c->type]->write(c.c, va, n, off); + poperror(); + + if(offp == nil && m < n){ + lock(cl); + c.c->offset -= n - m; + unlock(cl); + } + + poperror(); + cclose(c.c); + + poperror(); + return m; +} + +long +kwrite(int fd, void *va, long n) +{ + return rwrite(fd, va, n, nil); +} + +long +kpwrite(int fd, void *va, long n, vlong off) +{ + return rwrite(fd, va, n, &off); +} + +int +kwstat(char *path, uchar *buf, int n) +{ + volatile struct { Chan *c; } c; + + c.c = nil; + if(waserror()){ + cclose(c.c); + return -1; + } + validstat(buf, n); + c.c = namec(path, Aaccess, 0, 0); + n = devtab[c.c->type]->wstat(c.c, buf, n); + poperror(); + cclose(c.c); + return n; +} + +enum +{ + DIRSIZE = STATFIXLEN + 32 * 4, + DIRREADLIM = 2048, /* should handle the largest reasonable directory entry */ +}; + +Dir* +chandirstat(Chan *c) +{ + Dir *d; + uchar *buf; + int n, nd, i; + + nd = DIRSIZE; + for(i=0; i<2; i++){ /* should work by the second try */ + d = smalloc(sizeof(Dir) + nd); + buf = (uchar*)&d[1]; + if(waserror()){ + free(d); + return nil; + } + n = devtab[c->type]->stat(c, buf, nd); + poperror(); + if(n < BIT16SZ){ + free(d); + return nil; + } + nd = GBIT16((uchar*)buf) + BIT16SZ; /* size needed to store whole stat buffer including count */ + if(nd <= n){ + convM2D(buf, n, d, (char*)&d[1]); + return d; + } + /* else sizeof(Dir)+nd is plenty */ + free(d); + } + return nil; + +} + +Dir* +kdirstat(char *name) +{ + volatile struct {Chan *c;} c; + Dir *d; + + c.c = nil; + if(waserror()){ + cclose(c.c); + return nil; + } + c.c = namec(name, Aaccess, 0, 0); + d = chandirstat(c.c); + poperror(); + cclose(c.c); + return d; +} + +Dir* +kdirfstat(int fd) +{ + volatile struct { Chan *c; } c; + Dir *d; + + c.c = nil; + if(waserror()) { + cclose(c.c); + return nil; + } + c.c = fdtochan(up->env->fgrp, fd, -1, 0, 1); + d = chandirstat(c.c); + poperror(); + cclose(c.c); + return d; +} + +int +kdirwstat(char *name, Dir *dir) +{ + uchar *buf; + int r; + + r = sizeD2M(dir); + buf = smalloc(r); + convD2M(dir, buf, r); + r = kwstat(name, buf, r); + free(buf); + return r < 0? r: 0; +} + +int +kdirfwstat(int fd, Dir *dir) +{ + uchar *buf; + int r; + + r = sizeD2M(dir); + buf = smalloc(r); + convD2M(dir, buf, r); + r = kfwstat(fd, buf, r); + free(buf); + return r < 0? r: 0; +} + +static long +dirpackage(uchar *buf, long ts, Dir **d) +{ + char *s; + long ss, i, n, nn, m; + + *d = nil; + if(ts <= 0) + return ts; + + /* + * first find number of all stats, check they look like stats, & size all associated strings + */ + ss = 0; + n = 0; + for(i = 0; i < ts; i += m){ + m = BIT16SZ + GBIT16(&buf[i]); + if(statcheck(&buf[i], m) < 0) + break; + ss += m; + n++; + } + + if(i != ts) + error("bad directory format"); + + *d = malloc(n * sizeof(Dir) + ss); + if(*d == nil) + error(Enomem); + + /* + * then convert all buffers + */ + s = (char*)*d + n * sizeof(Dir); + nn = 0; + for(i = 0; i < ts; i += m){ + m = BIT16SZ + GBIT16((uchar*)&buf[i]); + if(nn >= n || convM2D(&buf[i], m, *d + nn, s) != m){ + free(*d); + *d = nil; + error("bad directory entry"); + } + nn++; + s += m; + } + + return nn; +} + +long +kdirread(int fd, Dir **d) +{ + uchar *buf; + long ts; + + *d = nil; + if(waserror()) + return -1; + buf = malloc(DIRREADLIM); + if(buf == nil) + error(Enomem); + if(waserror()){ + free(buf); + nexterror(); + } + ts = kread(fd, buf, DIRREADLIM); + if(ts > 0) + ts = dirpackage(buf, ts, d); + + poperror(); + free(buf); + poperror(); + return ts; +} + +int +kiounit(int fd) +{ + Chan *c; + int n; + + c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1); + if(waserror()){ + cclose(c); + return 0; /* n.b. */ + } + n = c->iounit; + poperror(); + cclose(c); + return n; +} diff --git a/emu/port/uqid.c b/emu/port/uqid.c new file mode 100644 index 00000000..bd43db3d --- /dev/null +++ b/emu/port/uqid.c @@ -0,0 +1,114 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +/* + * unique Qid path generation, + * for exportfs.c and various devfs-* + */ + +#define QIDMASK (((vlong)1<<48)-1) + +void +uqidinit(Uqidtab *tab) +{ + memset(tab, 0, sizeof(*tab)); +} + +static int +uqidhash(vlong path) +{ + ulong p; + p = (ulong)path; + return ((p>>16) ^ (p>>8) ^ p) & (Nqidhash-1); +} + +static Uqid ** +uqidlook(Uqid **tab, Chan *c, vlong path) +{ + Uqid **hp, *q; + + for(hp = &tab[uqidhash(path)]; (q = *hp) != nil; hp = &q->next) + if(q->type == c->type && q->dev == c->dev && q->oldpath == path) + break; + return hp; +} + +static int +uqidexists(Uqid **tab, vlong path) +{ + int i; + Uqid *q; + + for(i=0; i<Nqidhash; i++) + for(q = tab[i]; q != nil; q = q->next) + if(q->newpath == path) + return 1; + return 0; +} + +Uqid * +uqidalloc(Uqidtab *tab, Chan *c) +{ + Uqid **hp, *q; + + qlock(&tab->l); + hp = uqidlook(tab->qids, c, c->qid.path); + if((q = *hp) != nil){ + incref(&q->r); + qunlock(&tab->l); + return q; + } + q = mallocz(sizeof(*q), 1); + if(q == nil){ + qunlock(&tab->l); + error(Enomem); + } + q->r.ref = 1; + q->type = c->type; + q->dev = c->dev; + q->oldpath = c->qid.path; + q->newpath = c->qid.path; + while(uqidexists(tab->qids, q->newpath)){ + if(++tab->pathgen >= (1<<16)) + tab->pathgen = 1; + q->newpath = ((vlong)tab->pathgen<<48) | (q->newpath & QIDMASK); + } + q->next = nil; + *hp = q; + qunlock(&tab->l); + return q; +} + +void +freeuqid(Uqidtab *tab, Uqid *q) +{ + Uqid **hp; + + if(q == nil) + return; + qlock(&tab->l); + if(decref(&q->r) == 0){ + hp = &tab->qids[uqidhash(q->oldpath)]; + for(; *hp != nil; hp = &(*hp)->next) + if(*hp == q){ + *hp = q->next; + free(q); + break; + } + } + qunlock(&tab->l); +} + +Qid +mkuqid(Chan *c, Uqid *qid) +{ + Qid q; + + if(qid == nil) + return c->qid; + q.path = qid->newpath; + q.vers = c->qid.vers; + q.type = c->qid.type; + return q; +} diff --git a/emu/port/win-x11-old.c b/emu/port/win-x11-old.c new file mode 100644 index 00000000..aebcf199 --- /dev/null +++ b/emu/port/win-x11-old.c @@ -0,0 +1,845 @@ +#include "dat.h" +#include "fns.h" +#include "cursor.h" +#include "keyboard.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/Xutil.h> +#include <X11/keysym.h> + +#define ABS(x) ((x) < 0 ? -(x) : (x)) + +/* + * alias defs for image types to overcome name conflicts + */ +typedef struct ICursor ICursor; +typedef struct IPoint IPoint; +typedef struct IRectangle IRectangle; +typedef struct CRemapTbl CRemapTbl; +struct ICursor +{ + int w; + int h; + int hotx; + int hoty; + char *src; + char *mask; +}; + +struct IPoint +{ + int x; + int y; +}; + +struct IRectangle +{ + IPoint min; + IPoint max; +}; + +struct CRemapTbl +{ + ulong inferno[256]; /* The corresponding inferno colormap vals */ + ulong openslot[256]; + Bool cellused[256]; + int cnt; + int opencnt; +}; + +enum +{ + DblTime = 300 /* double click time in msec */ +}; + +XColor map[256]; /* Inferno colormap array */ +XColor map7[128]; /* Inferno colormap array */ +uchar map7to8[128][2]; +Colormap xcmap; /* Default shared colormap */ +int infernotox11[256]; /* Values for mapping between */ +int x11toinferno[256]; /* X11 and inferno */ +int x24bitswap = 0; /* swap endian for 24bit RGB */ + +static int triedscreen; +static XModifierKeymap *modmap; +static int keypermod; +static Drawable xdrawable; +static Atom wm_take_focus; +static void xexpose(XEvent*); +static void xmouse(XEvent*); +static void xkeyboard(XEvent*); +static void xmapping(XEvent*); +static void xproc(void*); +static void xinitscreen(int, int); +static void initmap(Window); +static GC creategc(Drawable); +static CRemapTbl crtbl; +static void graphicscmap(XColor*); + int xscreendepth; + Drawable xscreenid; + Display* xdisplay; + Display* xkmcon; + Visual *xvis; + GC xgcfill, xgccopy, xgcsimplesrc, xgczero, xgcreplsrc; + GC xgcfill0, xgccopy0, xgcsimplesrc0, xgczero0, xgcreplsrc0; + +char *gkscanid = "emu_x11"; + + +ulong* +attachscreen(IRectangle *r, int *ld, int *width, int *softscreen) +{ + extern ulong* makememones(); + + r->min.x = 0; + r->min.y = 0; + r->max.x = Xsize; + r->max.y = Ysize; + *ld = 3; + *width = Xsize/4; + *softscreen = 1; + if(!triedscreen){ + triedscreen = 1; + xinitscreen(Xsize, Ysize); + if(kproc("xproc", xproc, nil, 0) < 0) { + fprint(2, "emu: win-x11 can't make X proc\n"); + return 0; + } + } + return makememones(); +} + +void +flushmemscreen(IRectangle r) +{ + if(r.min.x > r.max.x) + return; + XCopyArea(xdisplay, xscreenid, xdrawable, xgccopy, + r.min.x, r.min.y, + r.max.x-r.min.x, r.max.y-r.min.y, r.min.x, r.min.y); + XFlush(xdisplay); +} + +static int +revbyte(int b) +{ + int r; + + r = 0; + r |= (b&0x01) << 7; + r |= (b&0x02) << 5; + r |= (b&0x04) << 3; + r |= (b&0x08) << 1; + r |= (b&0x10) >> 1; + r |= (b&0x20) >> 3; + r |= (b&0x40) >> 5; + r |= (b&0x80) >> 7; + return r; +} + +static void +gotcursor(ICursor c) +{ + Cursor xc; + XColor fg, bg; + Pixmap xsrc, xmask; + static Cursor xcursor; + + if(c.src == nil){ + if(xcursor != 0) { + XFreeCursor(xdisplay, xcursor); + xcursor = 0; + } + XUndefineCursor(xdisplay, xdrawable); + XFlush(xdisplay); + return; + } + xsrc = XCreateBitmapFromData(xdisplay, xdrawable, c.src, c.w, c.h); + xmask = XCreateBitmapFromData(xdisplay, xdrawable, c.mask, c.w, c.h); + + fg = map[255]; + bg = map[0]; + fg.pixel = infernotox11[255]; + bg.pixel = infernotox11[0]; + xc = XCreatePixmapCursor(xdisplay, xsrc, xmask, &fg, &bg, -c.hotx, -c.hoty); + if(xc != 0) { + XDefineCursor(xdisplay, xdrawable, xc); + if(xcursor != 0) + XFreeCursor(xdisplay, xcursor); + xcursor = xc; + } + XFreePixmap(xdisplay, xsrc); + XFreePixmap(xdisplay, xmask); + XFlush(xdisplay); + free(c.src); +} + +void +setcursor(IPoint p) +{ + XWarpPointer(xdisplay, None, xdrawable, 0, 0, 0, 0, p.x, p.y); + XFlush(xdisplay); +} + +void +drawcursor(Drawcursor* c) +{ + ICursor ic; + IRectangle ir; + uchar *bs, *bc; + int i, h, j, bpl; + char *src, *mask, *csrc, *cmask; + + /* Set the default system cursor */ + src = nil; + mask = nil; + if(c->data != nil){ + h = (c->maxy-c->miny)/2; + ir.min.x = c->minx; + ir.min.y = c->miny; + ir.max.x = c->maxx; + ir.max.y = c->maxy; + /* passing IRectangle to Rectangle is safe */ + bpl = bytesperline(ir, 1); + + i = h*bpl; + src = malloc(2*i); + if(src == nil) + return; + mask = src + i; + + csrc = src; + cmask = mask; + bc = c->data; + bs = c->data + h*bpl; + for(i = 0; i < h; i++){ + for(j = 0; j < bpl; j++) { + *csrc++ = revbyte(bs[j]); + *cmask++ = revbyte(bs[j] | bc[j]); + } + bs += bpl; + bc += bpl; + } + } + ic.w = 8*bpl; + ic.h = h; + ic.hotx = c->hotx; + ic.hoty = c->hoty; + ic.src = src; + ic.mask = mask; + + gotcursor(ic); +} + +static void +xproc(void *arg) +{ + ulong mask; + XEvent event; + + closepgrp(up->env->pgrp); + closefgrp(up->env->fgrp); + closeegrp(up->env->egrp); + closesigs(up->env->sigs); + + mask = KeyPressMask| + KeyReleaseMask| + ButtonPressMask| + ButtonReleaseMask| + PointerMotionMask| + Button1MotionMask| + Button2MotionMask| + Button3MotionMask| + ExposureMask; + + XSelectInput(xkmcon, xdrawable, mask); + for(;;) { + XWindowEvent(xkmcon, xdrawable, mask, &event); + switch(event.type) { + case KeyPress: + case KeyRelease: + xkeyboard(&event); + break; + case ButtonPress: + case ButtonRelease: + case MotionNotify: + xmouse(&event); + break; + case Expose: + xexpose(&event); + break; + case MappingNotify: + xmapping(&event); + break; + default: + break; + } + } +} + +static int +shutup(Display *d, XErrorEvent *e) +{ + char buf[200]; + print("X error: error code=%d, request_code=%d, minor=%d\n", e->error_code, e->request_code, e->minor_code); + XGetErrorText(d, e->error_code, buf, sizeof(buf)); + print("%s\n", buf); + USED(d); + USED(e); + return 0; +} + +static void +xinitscreen(int xsize, int ysize) +{ + int i, pmid; + char *argv[2]; + char *disp_val; + Window rootwin; + XWMHints hints; + Screen *screen; + XVisualInfo xvi; + int rootscreennum; + XTextProperty name; + XClassHint classhints; + XSizeHints normalhints; + XSetWindowAttributes attrs; + + xscreenid = 0; + xdrawable = 0; + + xdisplay = XOpenDisplay(NULL); + if(xdisplay == 0){ + disp_val = getenv("DISPLAY"); + if(disp_val == 0) + disp_val = "not set"; + fprint(2, "emu: win-x11 open %r, DISPLAY is %s\n", disp_val); + cleanexit(0); + } + + rootscreennum = DefaultScreen(xdisplay); + rootwin = DefaultRootWindow(xdisplay); + + xscreendepth = DefaultDepth(xdisplay, rootscreennum); + if(XMatchVisualInfo(xdisplay, rootscreennum, 24, TrueColor, &xvi) + || XMatchVisualInfo(xdisplay, rootscreennum, 24, DirectColor, &xvi)){ + xvis = xvi.visual; + xscreendepth = 24; + xtblbit = 1; + } + else if(XMatchVisualInfo(xdisplay, rootscreennum, 8, PseudoColor, &xvi) + || XMatchVisualInfo(xdisplay, rootscreennum, 8, StaticColor, &xvi)){ + if(xscreendepth > 8) { + fprint(2, "emu: win-x11 can't deal with depth %d screens\n", xscreendepth); + cleanexit(0); + } + xvis = xvi.visual; + xscreendepth = 8; + } + else{ + if(xscreendepth != 8){ + fprint(2, "emu: win-x11 can't deal with depth %d screens\n", xscreendepth); + cleanexit(0); + } + xvis = DefaultVisual(xdisplay, rootscreennum); + } + + screen = DefaultScreenOfDisplay(xdisplay); + xcmap = DefaultColormapOfScreen(screen); + + if(xvis->class != StaticColor){ + graphicscmap(map); + initmap(rootwin); + } + + if(modmap = XGetModifierMapping(xdisplay)) + keypermod = modmap->max_keypermod; + + attrs.colormap = xcmap; + attrs.background_pixel = 0; + attrs.border_pixel = 0; + /* attrs.override_redirect = 1;*/ /* WM leave me alone! |CWOverrideRedirect */ + xdrawable = XCreateWindow(xdisplay, rootwin, 0, 0, xsize, ysize, 0, xscreendepth, InputOutput, xvis, CWBackPixel|CWBorderPixel|CWColormap, &attrs); + + /* + * set up property as required by ICCCM + */ + name.value = "inferno"; + name.encoding = XA_STRING; + name.format = 8; + name.nitems = strlen(name.value); + normalhints.flags = USSize|PMaxSize; + normalhints.max_width = normalhints.width = xsize; + normalhints.max_height = normalhints.height = ysize; + hints.flags = InputHint|StateHint; + hints.input = 1; + hints.initial_state = NormalState; + classhints.res_name = "inferno"; + classhints.res_class = "Inferno"; + argv[0] = "inferno"; + argv[1] = nil; + XSetWMProperties(xdisplay, xdrawable, + &name, /* XA_WM_NAME property for ICCCM */ + &name, /* XA_WM_ICON_NAME */ + argv, /* XA_WM_COMMAND */ + 1, /* argc */ + &normalhints, /* XA_WM_NORMAL_HINTS */ + &hints, /* XA_WM_HINTS */ + &classhints); /* XA_WM_CLASS */ + + /* + * put the window on the screen + */ + XMapWindow(xdisplay, xdrawable); + + xscreenid = XCreatePixmap(xdisplay, xdrawable, xsize, ysize, xscreendepth); + XFlush(xdisplay); + + xgcfill = creategc(xscreenid); + XSetFillStyle(xdisplay, xgcfill, FillSolid); + xgccopy = creategc(xscreenid); + xgcsimplesrc = creategc(xscreenid); + XSetFillStyle(xdisplay, xgcsimplesrc, FillStippled); + xgczero = creategc(xscreenid); + xgcreplsrc = creategc(xscreenid); + XSetFillStyle(xdisplay, xgcreplsrc, FillTiled); + + pmid = XCreatePixmap(xdisplay, xdrawable, 1, 1, 1); + xgcfill0 = creategc(pmid); + XSetFillStyle(xdisplay, xgcfill0, FillSolid); + xgccopy0 = creategc(pmid); + xgcsimplesrc0 = creategc(pmid); + XSetFillStyle(xdisplay, xgcsimplesrc0, FillStippled); + xgczero0 = creategc(pmid); + xgcreplsrc0 = creategc(pmid); + XSetFillStyle(xdisplay, xgcreplsrc0, FillTiled); + XFreePixmap(xdisplay, pmid); + + XSetForeground(xdisplay, xgccopy, infernotox11[0]); + XFillRectangle(xdisplay, xscreenid, xgccopy, 0, 0, xsize, ysize); + + xkmcon = XOpenDisplay(NULL); + if(xkmcon == 0){ + disp_val = getenv("DISPLAY"); + if(disp_val == 0) + disp_val = "not set"; + fprint(2, "emu: win-x11 open %r, DISPLAY is %s\n", disp_val); + cleanexit(0); + } +} + +static void +graphicscmap(XColor *map) +{ + int r, g, b, cr, cg, cb, v, num, den, idx, v7, idx7; + + for(r=0; r!=4; r++) { + for(g = 0; g != 4; g++) { + for(b = 0; b!=4; b++) { + for(v = 0; v!=4; v++) { + den=r; + if(g > den) + den=g; + if(b > den) + den=b; + /* divide check -- pick grey shades */ + if(den==0) + cr=cg=cb=v*17; + else { + num=17*(4*den+v); + cr=r*num/den; + cg=g*num/den; + cb=b*num/den; + } + idx = r*64 + v*16 + ((g*4 + b + v - r) & 15); + idx = 255 - idx; + map[idx].red = cr*0x0101; + map[idx].green = cg*0x0101; + map[idx].blue = cb*0x0101; + map[idx].pixel = idx; + map[idx].flags = DoRed|DoGreen|DoBlue; + + v7 = v >> 1; + idx7 = r*32 + v7*16 + g*4 + b; + if((v & 1) == v7){ + map7to8[idx7][0] = idx; + if(den == 0) { /* divide check -- pick grey shades */ + cr = ((255.0/7.0)*v7)+0.5; + cg = cr; + cb = cr; + } + else { + num=17*15*(4*den+v7*2)/14; + cr=r*num/den; + cg=g*num/den; + cb=b*num/den; + } + map7[idx7].red = cr*0x0101; + map7[idx7].green = cg*0x0101; + map7[idx7].blue = cb*0x0101; + map7[idx7].pixel = idx7; + map7[idx7].flags = DoRed|DoGreen|DoBlue; + } + else + map7to8[idx7][1] = idx; + } + } + } + } +} + +/* + * Initialize and install the Inferno colormap as a private colormap for this + * application. Inferno gets the best colors here when it has the cursor focus. + */ +static void +initmap(Window w) +{ + XColor c; + XColor xcol; + ulong v; + int i, pix, ncmaps; + + if(xscreendepth <= 1) + return; + + if(xvis->class == TrueColor || xvis->class == DirectColor) { + int color_order_init = 0; + uint pp, p; + + for(i = 0; i < 256; i++) { + c = map[i]; + /* find out index into colormap for our RGB */ + if(!XAllocColor(xdisplay, xcmap, &c)) { + fprint(2, "emu: win-x11 can't alloc color\n"); + cleanexit(0); + } + + /* The pixel value returned from XGetPixel needs to + * be converted to RGB so we can call rgb2cmap() + * to translate between 24 bit X and our color. Unfortunately, + * the return value appears to be display server endian + * dependant. Therefore, we run some heuristics to later + * determine how to mask the int value correctly. + * Yeah, I know we can look at xvis->byte_order but + * some displays say MSB even though they run on LSB. + * Besides, this is more anal. + */ + + p = c.pixel; + pp = rgb2cmap((p>>16)&0xff,(p>>8)&0xff,p&0xff); + if(!color_order_init && (pp!=map[i].pixel)) { + /* check if endian is other way */ + pp = rgb2cmap(p&0xff,(p>>8)&0xff,(p>>16)&0xff); + if(pp!=map[i].pixel) { + fprint(2, "emu: win-x11 can't convert 24bit colors\n"); + cleanexit(0); + } + color_order_init = 1; + x24bitswap = 1; + } + + if(color_order_init) { + pp = rgb2cmap(p&0xff,(p>>8)&0xff,(p>>16)&0xff); + if(pp!=map[i].pixel) { + fprint(2, "emu: win-x11 can't convert 24bit colors\n"); + cleanexit(0); + } + /* no x11toinferno; later use rgb2cmap() */ + infernotox11[map[i].pixel] = c.pixel; + } + else if(pp!=map[i].pixel) { + fprint(2, "emu: win-x11 can't convert 24bit colors\n"); + cleanexit(0); + } + else { + /* no x11toinferno; later use rgb2cmap() */ + infernotox11[map[i].pixel] = c.pixel; + } + } + } + else if(xvis->class == PseudoColor) { + if(xtblbit == 0){ + xcmap = XCreateColormap(xdisplay, w, xvis, AllocAll); + XStoreColors(xdisplay, xcmap, map, 256); + for(i = 0; i < 256; i++) { + infernotox11[i] = i; + x11toinferno[i] = i; + } + } + else { + for(i = 0; i < 128; i++) { + c = map7[i]; + if(!XAllocColor(xdisplay, xcmap, &c)) { + fprint(2, "emu: win-x11 can't alloc colors in default map, don't use -7\n"); + cleanexit(0); + } + infernotox11[map7to8[i][0]] = c.pixel; + infernotox11[map7to8[i][1]] = c.pixel; + x11toinferno[c.pixel] = map7to8[i][0]; + } + } + } + else { + xtblbit = 0; + fprint(2, "emu: win-x11 unsupported visual class %d\n", xvis->class); + } + return; +} + +static void +xmapping(XEvent *e) +{ + XMappingEvent *xe; + + if(e->type != MappingNotify) + return; + xe = (XMappingEvent*)e; + if(modmap) + XFreeModifiermap(modmap); + modmap = XGetModifierMapping(xe->display); + if(modmap) + keypermod = modmap->max_keypermod; +} + + +/* + * Disable generation of GraphicsExpose/NoExpose events in the GC. + */ +static GC +creategc(Drawable d) +{ + XGCValues gcv; + + gcv.function = GXcopy; + gcv.graphics_exposures = False; + return XCreateGC(xdisplay, d, GCFunction|GCGraphicsExposures, &gcv); +} + +static void +xexpose(XEvent *e) +{ + IRectangle r; + XExposeEvent *xe; + + if(e->type != Expose) + return; + xe = (XExposeEvent*)e; + r.min.x = xe->x; + r.min.y = xe->y; + r.max.x = xe->x + xe->width; + r.max.y = xe->y + xe->height; + drawxflush(r); +} + + +static void +xkeyboard(XEvent *e) +{ + int ind, n; + KeySym k; + Rune r; + unsigned int md; + char buf[1]; + + if(gkscanq) { + uchar ch = (KeyCode)e->xkey.keycode; + if(e->xany.type == KeyRelease) + ch |= 0x80; + qproduce(gkscanq, &ch, 1); + return; + } + + /* + * I tried using XtGetActionKeysym, but it didn't seem to + * do case conversion properly + * (at least, with Xterminal servers and R4 intrinsics) + */ + if(e->xany.type != KeyPress) + return; + + md = e->xkey.state; + ind = 0; + if(md & ShiftMask) + ind = 1; + + k = XKeycodeToKeysym(e->xany.display, (KeyCode)e->xkey.keycode, ind); + + /* May have to try unshifted version */ + if(k == NoSymbol && ind == 1) + k = XKeycodeToKeysym(e->xany.display, (KeyCode)e->xkey.keycode, 0); + + if(k == XK_Multi_key || k == NoSymbol) + return; + if(k&0xFF00){ + switch(k){ + case XK_BackSpace: + case XK_Tab: + case XK_Escape: + case XK_Delete: + case XK_KP_0: + case XK_KP_1: + case XK_KP_2: + case XK_KP_3: + case XK_KP_4: + case XK_KP_5: + case XK_KP_6: + case XK_KP_7: + case XK_KP_8: + case XK_KP_9: + case XK_KP_Divide: + case XK_KP_Multiply: + case XK_KP_Subtract: + case XK_KP_Add: + case XK_KP_Decimal: + k &= 0x7F; + break; + case XK_Linefeed: + k = '\r'; + break; + case XK_KP_Enter: + case XK_Return: + k = '\n'; + break; + case XK_Alt_L: + case XK_Alt_R: + case XK_Meta_L: + case XK_Meta_R: + k = Latin; + break; + case XK_Left: + case XK_KP_Left: + k = Left; + break; + case XK_Down: + case XK_KP_Down: + k = Down; + break; + case XK_Right: + case XK_KP_Right: + k = Right; + break; + case XK_Up: + case XK_KP_Up: + k = Up; + break; + case XK_Home: + case XK_KP_Home: + k = Home; + break; + case XK_End: + case XK_KP_End: + k = End; + break; + case XK_Page_Up: + case XK_KP_Page_Up: + k = Pgup; + break; + case XK_Page_Down: + case XK_KP_Page_Down: + k = Pgdown; + break; + default: /* not ISO-1 or tty control */ + return; + } + } + /* Compensate for servers that call a minus a hyphen */ + if(k == XK_hyphen) + k = XK_minus; + /* Do control mapping ourselves if translator doesn't */ + if(md & ControlMask) + k &= 0x9f; + if(k == '\t' && ind) + k = BackTab; + + if(md & Mod1Mask) + k = APP|(k&0xff); + if(k == NoSymbol) + return; + + gkbdputc(gkbdq, k); +} + +static void +xmouse(XEvent *e) +{ + int s, dbl; + XButtonEvent *be; + XMotionEvent *me; + XEvent motion; + Pointer m; + static ulong lastb, lastt; + + dbl = 0; + switch(e->type){ + case ButtonPress: + be = (XButtonEvent *)e; + m.x = be->x; + m.y = be->y; + s = be->state; + if(be->button == lastb && be->time - lastt < DblTime) + dbl = 1; + lastb = be->button; + lastt = be->time; + switch(be->button){ + case 1: + s |= Button1Mask; + break; + case 2: + s |= Button2Mask; + break; + case 3: + s |= Button3Mask; + break; + } + break; + case ButtonRelease: + be = (XButtonEvent *)e; + m.x = be->x; + m.y = be->y; + s = be->state; + switch(be->button){ + case 1: + s &= ~Button1Mask; + break; + case 2: + s &= ~Button2Mask; + break; + case 3: + s &= ~Button3Mask; + break; + } + break; + case MotionNotify: + me = (XMotionEvent *) e; + + /* remove excess MotionNotify events from queue and keep last one */ + while(XCheckTypedWindowEvent(xkmcon, xdrawable, MotionNotify, &motion) == True) + me = (XMotionEvent *) &motion; + + s = me->state; + m.x = me->x; + m.y = me->y; + break; + default: + return; + } + + m.b = 0; + if(s & Button1Mask) + m.b |= 1; + if(s & Button2Mask) + m.b |= 2; + if(s & Button3Mask) + m.b |= 4; + if(dbl) + m.b |= 1<<4; + + m.modify = 1; + mouseproduce(m); +} + diff --git a/emu/port/win-x11a.c b/emu/port/win-x11a.c new file mode 100644 index 00000000..19c7a179 --- /dev/null +++ b/emu/port/win-x11a.c @@ -0,0 +1,1424 @@ +/* + * This implementation of the screen functions for X11 uses the + * portable implementation of the Inferno drawing operations (libmemdraw) + * to do the work, then has flushmemscreen copy the result to the X11 display. + * Thus it potentially supports all colour depths but with a possible + * performance penalty (although it tries to use the X11 shared memory extension + * to copy the result to the screen, which might reduce the latter). + * + * CraigN + */ + +#define _GNU_SOURCE 1 +#include "dat.h" +#include "fns.h" +#undef log2 +#include <draw.h> +#include "cursor.h" +#include "keyboard.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#define Colormap XColormap +#define Cursor XCursor +#define Display XDisplay +#define Drawable XDrawable +#define Font XFont +#define GC XGC +#define Point XPoint +#define Rectangle XRectangle +#define Screen XScreen +#define Visual XVisual +#define Window XWindow + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/Xutil.h> +#include <X11/keysym.h> +#include <X11/extensions/XShm.h> + +#include "keysym2ucs.h" + +#undef Colormap +#undef Cursor +#undef Display +#undef XDrawable +#undef Font +#undef GC +#undef Point +#undef Rectangle +#undef Screen +#undef Visual +#undef Window + +#include <sys/ipc.h> +#include <sys/shm.h> + +static int displaydepth; +extern ulong displaychan; + +enum +{ + DblTime = 300 /* double click time in msec */ +}; + +/* screen data .... */ +static uchar* gscreendata; +static uchar* xscreendata; + +XColor map[256]; /* Inferno colormap array */ +XColor mapr[256]; /* Inferno red colormap array */ +XColor mapg[256]; /* Inferno green colormap array */ +XColor mapb[256]; /* Inferno blue colormap array */ +XColor map7[128]; /* Inferno colormap array */ +uchar map7to8[128][2]; + +/* for copy/paste, lifted from plan9ports via drawterm */ +static Atom clipboard; +static Atom utf8string; +static Atom targets; +static Atom text; +static Atom compoundtext; + +static Atom cursorchange; + +static XColormap xcmap; /* Default shared colormap */ +static int infernotox11[256]; /* Values for mapping between */ +static int infernortox11[256]; /* Values for mapping between */ +static int infernogtox11[256]; /* Values for mapping between */ +static int infernobtox11[256]; /* Values for mapping between */ +static int triedscreen; +static XDrawable xdrawable; +static void xexpose(XEvent*); +static void xmouse(XEvent*); +static void xkeyboard(XEvent*); +static void xsetcursor(XEvent*); +static void xkbdproc(void*); +static void xdestroy(XEvent*); +static void xselect(XEvent*, XDisplay*); +static void xproc(void*); +static void xinitscreen(int, int, ulong, ulong*, int*); +static void initmap(XWindow, ulong, ulong*, int*); +static XGC creategc(XDrawable); +static void graphicsgmap(XColor*, int); +static void graphicscmap(XColor*); +static void graphicsrgbmap(XColor*, XColor*, XColor*); +static int xscreendepth; +static XDisplay* xdisplay; /* used holding draw lock */ +static XDisplay* xmcon; /* used only in xproc */ +static XDisplay* xkbdcon; /* used only in xkbdproc */ +static XDisplay* xsnarfcon; /* used holding clip.lk */ +static XVisual *xvis; +static XGC xgc; +static XImage *img; +static int is_shm; +static XShmSegmentInfo *shminfo; + +static int putsnarf, assertsnarf; + +/* + * The documentation for the XSHM extension implies that if the server + * supports XSHM but is not the local machine, the XShm calls will + * return False; but this turns out not to be the case. Instead, the + * server throws a BadAccess error. So, we need to catch X errors + * around all of our XSHM calls, sigh. + */ +static int shm_got_x_error = False; +static XErrorHandler old_handler = 0; +static XErrorHandler old_io_handler = 0; + +static int +shm_ehandler(XDisplay *dpy, XErrorEvent *error) +{ + shm_got_x_error = 1; + return 0; +} + +static void +clean_errhandlers(void) +{ + /* remove X11 error handler(s) */ + if(old_handler) + XSetErrorHandler(old_handler); + old_handler = 0; + if(old_io_handler) + XSetErrorHandler(old_io_handler); + old_io_handler = 0; +} + +uchar* +attachscreen(Rectangle *r, ulong *chan, int *d, int *width, int *softscreen) +{ + ulong c; + int depth; + + Xsize &= ~0x3; /* ensure multiple of 4 */ + + r->min.x = 0; + r->min.y = 0; + r->max.x = Xsize; + r->max.y = Ysize; + + c = displaychan; + if(c == 0) + c = CMAP8; + + if(!triedscreen){ + xinitscreen(Xsize, Ysize, c, chan, d); + /* + * moved xproc from here to end since it could cause an expose event and + * hence a flushmemscreen before xscreendata is initialized + */ + } + else{ + *chan = displaychan; + *d = displaydepth; + } + + *width = (Xsize/4)*(*d/8); + *softscreen = 1; + displaychan = *chan; + displaydepth = *d; + + /* check for X Shared Memory Extension */ + is_shm = XShmQueryExtension(xdisplay); + + if(is_shm) { + shminfo = malloc(sizeof(XShmSegmentInfo)); + if(shminfo == nil) { + fprint(2, "emu: cannot allocate XShmSegmentInfo\n"); + cleanexit(0); + } + + /* setup to catch X11 error(s) */ + XSync(xdisplay, 0); + shm_got_x_error = 0; + if(old_handler != shm_ehandler) + old_handler = XSetErrorHandler(shm_ehandler); + if(old_io_handler != shm_ehandler) + old_io_handler = XSetErrorHandler(shm_ehandler); + + img = XShmCreateImage(xdisplay, xvis, xscreendepth, ZPixmap, + NULL, shminfo, Xsize, Ysize); + XSync(xdisplay, 0); + + /* did we get an X11 error? if so then try without shm */ + if(shm_got_x_error) { + is_shm = 0; + free(shminfo); + shminfo = NULL; + clean_errhandlers(); + goto next; + } + + if(img == nil) { + fprint(2, "emu: can not allocate virtual screen buffer\n"); + cleanexit(0); + } + + shminfo->shmid = shmget(IPC_PRIVATE, img->bytes_per_line * img->height, + IPC_CREAT|0777); + shminfo->shmaddr = img->data = shmat(shminfo->shmid, 0, 0); + shminfo->readOnly = True; + + if(!XShmAttach(xdisplay, shminfo)) { + fprint(2, "emu: cannot allocate virtual screen buffer\n"); + cleanexit(0); + } + XSync(xdisplay, 0); + + /* + * Delete the shared segment right now; the segment + * won't actually go away until both the client and + * server have deleted it. The server will delete it + * as soon as the client disconnects, so we might as + * well delete our side now as later. + */ + shmctl(shminfo->shmid, IPC_RMID, 0); + + /* did we get an X11 error? if so then try without shm */ + if(shm_got_x_error) { + is_shm = 0; + XDestroyImage(img); + XSync(xdisplay, 0); + free(shminfo); + shminfo = NULL; + clean_errhandlers(); + goto next; + } + + gscreendata = malloc(Xsize * Ysize * (displaydepth >> 3)); + if(gscreendata == nil) { + fprint(2, "emu: cannot allocate screen buffer (%dx%d)\n", Xsize*Ysize); + cleanexit(0); + } + xscreendata = (uchar*)img->data; + + clean_errhandlers(); + } + next: + if(!is_shm) { + depth = xscreendepth; + if(depth == 24) + depth = 32; + + /* allocate virtual screen */ + gscreendata = malloc(Xsize * Ysize * (displaydepth >> 3)); + xscreendata = malloc(Xsize * Ysize * (depth >> 3)); + if(!gscreendata || !xscreendata) { + fprint(2, "emu: can not allocate virtual screen buffer\n"); + return 0; + } + img = XCreateImage(xdisplay, xvis, xscreendepth, ZPixmap, 0, + (char*)xscreendata, Xsize, Ysize, 8, Xsize * (depth >> 3)); + if(img == nil) { + fprint(2, "emu: can not allocate virtual screen buffer\n"); + return 0; + } + + } + + if(!triedscreen){ + triedscreen = 1; + kproc("xproc", xproc, xmcon, 0); + kproc("xkbdproc", xkbdproc, xkbdcon, KPX11); /* silly stack size for bloated X11 */ + } + + return gscreendata; +} + +void +flushmemscreen(Rectangle r) +{ + int x, y, width, height, dx; + uchar *p, *ep, *cp; + + // Clip to screen + if(r.min.x < 0) + r.min.x = 0; + if(r.min.y < 0) + r.min.y = 0; + if(r.max.x >= Xsize) + r.max.x = Xsize - 1; + if(r.max.y >= Ysize) + r.max.y = Ysize - 1; + + // is there anything left ... + width = r.max.x-r.min.x; + height = r.max.y-r.min.y; + if((width < 1) | (height < 1)) + return; + + // Blit the pixel data ... + if(displaydepth == 32){ + u32int v, w, *dp, *wp, *edp; + + dx = Xsize - width; + dp = (unsigned int *)(gscreendata + (r.min.y * Xsize + r.min.x) * 4); + wp = (unsigned int *)(xscreendata + (r.min.y * Xsize + r.min.x) * 4); + edp = (unsigned int *)(gscreendata + (r.max.y * Xsize + r.max.x) * 4); + while (dp < edp) { + const unsigned int *lp = dp + width; + + while (dp < lp){ + v = *dp++; + w = infernortox11[(v>>16)&0xff]<<16|infernogtox11[(v>>8)&0xff]<<8|infernobtox11[(v>>0)&0xff]<<0; + *wp++ = w; + } + + dp += dx; + wp += dx; + } + } + else if(displaydepth == 8){ + if(xscreendepth == 24 || xscreendepth == 32) { + u32int *wp; + + dx = Xsize - width; + p = gscreendata + r.min.y * Xsize + r.min.x; + wp = (u32int *)(xscreendata + (r.min.y * Xsize + r.min.x) * 4); + ep = gscreendata + r.max.y * Xsize + r.max.x; + while (p < ep) { + const uchar *lp = p + width; + + while (p < lp) + *wp++ = infernotox11[*p++]; + + p += dx; + wp += dx; + } + + } else if(xscreendepth == 24) { + int v; + + dx = Xsize - width; + p = gscreendata + r.min.y * Xsize + r.min.x; + cp = xscreendata + (r.min.y * Xsize + r.min.x) * 3; + ep = gscreendata + r.max.y * Xsize + r.max.x; + while (p < ep) { + const uchar *lp = p + width; + + while (p < lp){ + v = infernotox11[*p++]; + cp[0] = (v>>16)&0xff; + cp[1] = (v>>8)&0xff; + cp[2] = (v>>0)&0xff; + cp += 3; + } + + p += dx; + cp += 3*dx; + } + + } else if(xscreendepth == 16) { + u16int *sp; + + dx = Xsize - width; + p = gscreendata + r.min.y * Xsize + r.min.x; + sp = (unsigned short *)(xscreendata + (r.min.y * Xsize + r.min.x) * 2); + ep = gscreendata + r.max.y * Xsize + r.max.x; + while (p < ep) { + const uchar *lp = p + width; + + while (p < lp) + *sp++ = infernotox11[*p++]; + + p += dx; + sp += dx; + } + + } else if(xscreendepth == 8) { + + dx = Xsize - width; + p = gscreendata + r.min.y * Xsize + r.min.x; + cp = xscreendata + r.min.y * Xsize + r.min.x; + ep = gscreendata + r.max.y * Xsize + r.max.x; + while (p < ep) { + const uchar *lp = p + width; + + while (p < lp) + *cp++ = infernotox11[*p++]; + + p += dx; + cp += dx; + } + + } else { + for (y = r.min.y; y < r.max.y; y++) { + x = r.min.x; + p = gscreendata + y * Xsize + x; + while (x < r.max.x) + XPutPixel(img, x++, y, infernotox11[*p++]); + } + } + } + else{ + fprint(2, "emu: bad display depth %d\n", displaydepth); + cleanexit(0); + } + + /* Display image on X11 */ + if(is_shm) + XShmPutImage(xdisplay, xdrawable, xgc, img, r.min.x, r.min.y, r.min.x, r.min.y, width, height, 0); + else + XPutImage(xdisplay, xdrawable, xgc, img, r.min.x, r.min.y, r.min.x, r.min.y, width, height); + XSync(xdisplay, 0); +} + +static int +revbyte(int b) +{ + int r; + + r = 0; + r |= (b&0x01) << 7; + r |= (b&0x02) << 5; + r |= (b&0x04) << 3; + r |= (b&0x08) << 1; + r |= (b&0x10) >> 1; + r |= (b&0x20) >> 3; + r |= (b&0x40) >> 5; + r |= (b&0x80) >> 7; + return r; +} + +void +setpointer(int x, int y) +{ + drawqlock(); + XWarpPointer(xdisplay, None, xdrawable, 0, 0, 0, 0, x, y); + XFlush(xdisplay); + drawqunlock(); +} + +static void +xkbdproc(void *arg) +{ + XEvent event; + XDisplay *xd; + + xd = arg; + + /* BEWARE: the value of up is not defined for this proc on some systems */ + + XSelectInput(xd, xdrawable, KeyPressMask); + for(;;){ + XNextEvent(xd, &event); + xkeyboard(&event); + xsetcursor(&event); + } +} + +static void +xproc(void *arg) +{ + ulong mask; + XEvent event; + XDisplay *xd; + + closepgrp(up->env->pgrp); + closefgrp(up->env->fgrp); + closeegrp(up->env->egrp); + closesigs(up->env->sigs); + + xd = arg; + mask = ButtonPressMask| + ButtonReleaseMask| + PointerMotionMask| + Button1MotionMask| + Button2MotionMask| + Button3MotionMask| + Button4MotionMask| + Button5MotionMask| + ExposureMask| + StructureNotifyMask; + + XSelectInput(xd, xdrawable, mask); + for(;;){ + XNextEvent(xd, &event); + xselect(&event, xd); + xmouse(&event); + xexpose(&event); + xdestroy(&event); + } +} + +/* + * this crud is here because X11 can put huge amount of data + * on the stack during keyboard translation and cursor changing(!). + * we do both in a dedicated process with lots of stack, perhaps even enough. + */ + +enum { + CursorSize= 32 /* biggest cursor size */ +}; + +typedef struct ICursor ICursor; +struct ICursor { + ulong inuse; + int modify; + int hotx; + int hoty; + int w; + int h; + uchar src[(CursorSize/8)*CursorSize]; /* image and mask bitmaps */ + uchar mask[(CursorSize/8)*CursorSize]; +}; +static ICursor icursor; + +static void +xcurslock(void) +{ + while(_tas(&icursor.inuse) != 0) + osyield(); +} + +static void +xcursunlock(void) +{ + icursor.inuse = 0; +} + +static void +xcursnotify(void) +{ + XClientMessageEvent e; + + memset(&e, 0, sizeof e); + e.type = ClientMessage; + e.window = xdrawable; + e.message_type = cursorchange; + e.format = 8; + XSendEvent(xkbdcon, xdrawable, True, KeyPressMask, (XEvent*)&e); + XFlush(xkbdcon); +} + +void +drawcursor(Drawcursor* c) +{ + uchar *bs, *bc, *ps, *pm; + int i, j, w, h, bpl; + + if(c->data == nil){ + drawqlock(); + if(icursor.h != 0){ + xcurslock(); + icursor.h = 0; + icursor.modify = 1; + xcursunlock(); + } + xcursnotify(); + drawqunlock(); + return; + } + + drawqlock(); + xcurslock(); + icursor.modify = 0; /* xsetcursor will now ignore it */ + xcursunlock(); + + h = (c->maxy-c->miny)/2; /* image, then mask */ + bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1); + w = bpl; + if(w > CursorSize/8) + w = CursorSize/8; + + ps = icursor.src; + pm = icursor.mask; + bc = c->data; + bs = c->data + h*bpl; + for(i = 0; i < h; i++){ + for(j = 0; j < bpl && j < w; j++) { + *ps++ = revbyte(bs[j]); + *pm++ = revbyte(bs[j] | bc[j]); + } + bs += bpl; + bc += bpl; + } + icursor.h = h; + icursor.w = w*8; + icursor.hotx = c->hotx; + icursor.hoty = c->hoty; + icursor.modify = 1; + xcursnotify(); + drawqunlock(); +} + +static void +xsetcursor(XEvent *e) +{ + ICursor ic; + XCursor xc; + XColor fg, bg; + Pixmap xsrc, xmask; + static XCursor xcursor; + + if(e->type != ClientMessage || !e->xclient.send_event || e->xclient.message_type != cursorchange) + return; + + xcurslock(); + if(icursor.modify == 0){ + xcursunlock(); + return; + } + icursor.modify = 0; + if(icursor.h == 0){ + xcursunlock(); + /* set the default system cursor */ + if(xcursor != 0) { + XFreeCursor(xkbdcon, xcursor); + xcursor = 0; + } + XUndefineCursor(xkbdcon, xdrawable); + XFlush(xkbdcon); + return; + } + ic = icursor; + xcursunlock(); + + xsrc = XCreateBitmapFromData(xkbdcon, xdrawable, (char*)ic.src, ic.w, ic.h); + xmask = XCreateBitmapFromData(xkbdcon, xdrawable, (char*)ic.mask, ic.w, ic.h); + + fg = map[0]; + bg = map[255]; + fg.pixel = infernotox11[0]; + bg.pixel = infernotox11[255]; + xc = XCreatePixmapCursor(xkbdcon, xsrc, xmask, &fg, &bg, -ic.hotx, -ic.hoty); + if(xc != 0) { + XDefineCursor(xkbdcon, xdrawable, xc); + if(xcursor != 0) + XFreeCursor(xkbdcon, xcursor); + xcursor = xc; + } + XFreePixmap(xkbdcon, xsrc); + XFreePixmap(xkbdcon, xmask); + XFlush(xkbdcon); +} + +static void +xinitscreen(int xsize, int ysize, ulong c, ulong *chan, int *d) +{ + char *argv[2]; + char *dispname; + XWindow rootwin; + XWMHints hints; + XScreen *screen; + int rootscreennum; + XTextProperty name; + XClassHint classhints; + XSizeHints normalhints; + XSetWindowAttributes attrs; + + xdrawable = 0; + + dispname = getenv("DISPLAY"); + if(dispname == nil) + dispname = "not set"; + xdisplay = XOpenDisplay(NULL); + if(xdisplay == 0){ + fprint(2, "emu: win-x11 open %r, DISPLAY is %s\n", dispname); + cleanexit(0); + } + + rootscreennum = DefaultScreen(xdisplay); + rootwin = DefaultRootWindow(xdisplay); + xscreendepth = DefaultDepth(xdisplay, rootscreennum); + xvis = DefaultVisual(xdisplay, rootscreennum); + screen = DefaultScreenOfDisplay(xdisplay); + xcmap = DefaultColormapOfScreen(screen); + + *chan = CMAP8; + *d = 8; + + if(xvis->class != StaticColor) { + if(TYPE(c) == CGrey) + graphicsgmap(map, NBITS(c)); + else{ + graphicscmap(map); + graphicsrgbmap(mapr, mapg, mapb); + } + initmap(rootwin, c, chan, d); + } + + attrs.colormap = xcmap; + attrs.background_pixel = 0; + attrs.border_pixel = 0; + /* attrs.override_redirect = 1;*/ /* WM leave me alone! |CWOverrideRedirect */ + xdrawable = XCreateWindow(xdisplay, rootwin, 0, 0, xsize, ysize, 0, xscreendepth, + InputOutput, xvis, CWBackPixel|CWBorderPixel|CWColormap, &attrs); + + /* + * set up property as required by ICCCM + */ + name.value = (uchar*)"inferno"; + name.encoding = XA_STRING; + name.format = 8; + name.nitems = strlen((char*)name.value); + normalhints.flags = USSize|PMaxSize; + normalhints.max_width = normalhints.width = xsize; + normalhints.max_height = normalhints.height = ysize; + hints.flags = InputHint|StateHint; + hints.input = 1; + hints.initial_state = NormalState; + classhints.res_name = "inferno"; + classhints.res_class = "Inferno"; + argv[0] = "inferno"; + argv[1] = nil; + XSetWMProperties(xdisplay, xdrawable, + &name, /* XA_WM_NAME property for ICCCM */ + &name, /* XA_WM_ICON_NAME */ + argv, /* XA_WM_COMMAND */ + 1, /* argc */ + &normalhints, /* XA_WM_NORMAL_HINTS */ + &hints, /* XA_WM_HINTS */ + &classhints); /* XA_WM_CLASS */ + + XMapWindow(xdisplay, xdrawable); + XFlush(xdisplay); + + xgc = creategc(xdrawable); + + xmcon = XOpenDisplay(NULL); + xsnarfcon = XOpenDisplay(NULL); + xkbdcon = XOpenDisplay(NULL); + if(xmcon == 0 || xsnarfcon == 0 || xkbdcon == 0){ + fprint(2, "emu: win-x11 open %r, DISPLAY is %s\n", dispname); + cleanexit(0); + } + + clipboard = XInternAtom(xmcon, "CLIPBOARD", False); + utf8string = XInternAtom(xmcon, "UTF8_STRING", False); + targets = XInternAtom(xmcon, "TARGETS", False); + text = XInternAtom(xmcon, "TEXT", False); + compoundtext = XInternAtom(xmcon, "COMPOUND_TEXT", False); + + cursorchange = XInternAtom(xkbdcon, "TheCursorHasChanged", False); + +} + +static void +graphicsgmap(XColor *map, int d) +{ + int i, j, s, m, p; + + s = 8-d; + m = 1; + while(--d >= 0) + m *= 2; + m = 255/(m-1); + for(i=0; i < 256; i++){ + j = (i>>s)*m; + p = 255-i; + map[p].red = map[p].green = map[p].blue = (255-j)*0x0101; + map[p].pixel = p; + map[p].flags = DoRed|DoGreen|DoBlue; + } +} + +static void +graphicscmap(XColor *map) +{ + int r, g, b, cr, cg, cb, v, num, den, idx, v7, idx7; + + for(r=0; r!=4; r++) { + for(g = 0; g != 4; g++) { + for(b = 0; b!=4; b++) { + for(v = 0; v!=4; v++) { + den=r; + if(g > den) + den=g; + if(b > den) + den=b; + /* divide check -- pick grey shades */ + if(den==0) + cr=cg=cb=v*17; + else { + num=17*(4*den+v); + cr=r*num/den; + cg=g*num/den; + cb=b*num/den; + } + idx = r*64 + v*16 + ((g*4 + b + v - r) & 15); + /* was idx = 255 - idx; */ + map[idx].red = cr*0x0101; + map[idx].green = cg*0x0101; + map[idx].blue = cb*0x0101; + map[idx].pixel = idx; + map[idx].flags = DoRed|DoGreen|DoBlue; + + v7 = v >> 1; + idx7 = r*32 + v7*16 + g*4 + b; + if((v & 1) == v7){ + map7to8[idx7][0] = idx; + if(den == 0) { /* divide check -- pick grey shades */ + cr = ((255.0/7.0)*v7)+0.5; + cg = cr; + cb = cr; + } + else { + num=17*15*(4*den+v7*2)/14; + cr=r*num/den; + cg=g*num/den; + cb=b*num/den; + } + map7[idx7].red = cr*0x0101; + map7[idx7].green = cg*0x0101; + map7[idx7].blue = cb*0x0101; + map7[idx7].pixel = idx7; + map7[idx7].flags = DoRed|DoGreen|DoBlue; + } + else + map7to8[idx7][1] = idx; + } + } + } + } +} + +static void +graphicsrgbmap(XColor *mapr, XColor *mapg, XColor *mapb) +{ + int i; + + memset(mapr, 0, 256*sizeof(XColor)); + memset(mapg, 0, 256*sizeof(XColor)); + memset(mapb, 0, 256*sizeof(XColor)); + for(i=0; i < 256; i++){ + mapr[i].red = mapg[i].green = mapb[i].blue = i*0x0101; + mapr[i].pixel = mapg[i].pixel = mapb[i].pixel = i; + mapr[i].flags = mapg[i].flags = mapb[i].flags = DoRed|DoGreen|DoBlue; + } +} + +/* + * Initialize and install the Inferno colormap as a private colormap for this + * application. Inferno gets the best colors here when it has the cursor focus. + */ +static void +initmap(XWindow w, ulong cc, ulong *chan, int *d) +{ + XColor c; + int i; + + if(xscreendepth <= 1) + return; + + if(xvis->class == TrueColor || xvis->class == DirectColor) { + for(i = 0; i < 256; i++) { + c = map[i]; + /* find out index into colormap for our RGB */ + if(!XAllocColor(xdisplay, xcmap, &c)) { + fprint(2, "emu: win-x11 can't alloc color\n"); + cleanexit(0); + } + infernotox11[map[i].pixel] = c.pixel; + if(xscreendepth >= 24){ + c = mapr[i]; + XAllocColor(xdisplay, xcmap, &c); + infernortox11[i] = (c.pixel>>16)&0xff; + c = mapg[i]; + XAllocColor(xdisplay, xcmap, &c); + infernogtox11[i] = (c.pixel>>8)&0xff; + c = mapb[i]; + XAllocColor(xdisplay, xcmap, &c); + infernobtox11[i] = (c.pixel>>0)&0xff; + } + } + if(TYPE(cc) != CGrey && cc != CMAP8 && xscreendepth >= 24){ + *chan = XRGB32; + *d = 32; + } + } + else if(xvis->class == PseudoColor) { + if(xtblbit == 0){ + xcmap = XCreateColormap(xdisplay, w, xvis, AllocAll); + XStoreColors(xdisplay, xcmap, map, 256); + for(i = 0; i < 256; i++) + infernotox11[i] = i; + } else { + for(i = 0; i < 128; i++) { + c = map7[i]; + if(!XAllocColor(xdisplay, xcmap, &c)) { + fprint(2, "emu: win-x11 can't alloc colors in default map, don't use -7\n"); + cleanexit(0); + } + infernotox11[map7to8[i][0]] = c.pixel; + infernotox11[map7to8[i][1]] = c.pixel; + } + } + } + else { + xtblbit = 0; + fprint(2, "emu: win-x11 unsupported visual class %d\n", xvis->class); + } + return; +} + +static void +xdestroy(XEvent *e) +{ + XDestroyWindowEvent *xe; + if(e->type != DestroyNotify) + return; + xe = (XDestroyWindowEvent*)e; + if(xe->window == xdrawable) + cleanexit(0); +} + +/* + * Disable generation of GraphicsExpose/NoExpose events in the XGC. + */ +static XGC +creategc(XDrawable d) +{ + XGCValues gcv; + + gcv.function = GXcopy; + gcv.graphics_exposures = False; + return XCreateGC(xdisplay, d, GCFunction|GCGraphicsExposures, &gcv); +} + +static void +xexpose(XEvent *e) +{ + Rectangle r; + XExposeEvent *xe; + + if(e->type != Expose) + return; + xe = (XExposeEvent*)e; + r.min.x = xe->x; + r.min.y = xe->y; + r.max.x = xe->x + xe->width; + r.max.y = xe->y + xe->height; + drawqlock(); + flushmemscreen(r); + drawqunlock(); +} + +static void +xkeyboard(XEvent *e) +{ + int ind, md; + KeySym k; + + if(e->type == KeyPress && gkscanq != nil){ + uchar ch = e->xkey.keycode; + if(e->xany.type == KeyRelease) + ch |= 0x80; + qproduce(gkscanq, &ch, 1); + return; + } + + /* + * I tried using XtGetActionKeysym, but it didn't seem to + * do case conversion properly + * (at least, with Xterminal servers and R4 intrinsics) + */ + if(e->xany.type != KeyPress) + return; + + md = e->xkey.state; + ind = 0; + if(md & ShiftMask) + ind = 1; + if(0){ + k = XKeycodeToKeysym(e->xany.display, (KeyCode)e->xkey.keycode, ind); + + /* May have to try unshifted version */ + if(k == NoSymbol && ind == 1) + k = XKeycodeToKeysym(e->xany.display, (KeyCode)e->xkey.keycode, 0); + }else + XLookupString((XKeyEvent*)e, NULL, 0, &k, NULL); + + if(k == XK_Multi_key || k == NoSymbol) + return; + if(k&0xFF00){ + switch(k){ + case XK_BackSpace: + case XK_Tab: + case XK_Escape: + case XK_Delete: + case XK_KP_0: + case XK_KP_1: + case XK_KP_2: + case XK_KP_3: + case XK_KP_4: + case XK_KP_5: + case XK_KP_6: + case XK_KP_7: + case XK_KP_8: + case XK_KP_9: + case XK_KP_Divide: + case XK_KP_Multiply: + case XK_KP_Subtract: + case XK_KP_Add: + case XK_KP_Decimal: + k &= 0x7F; + break; + case XK_Linefeed: + k = '\r'; + break; + case XK_KP_Space: + k = ' '; + break; +// case XK_Home: +// case XK_KP_Home: +// k = Khome; +// break; + case XK_Left: + case XK_KP_Left: + k = Left; + break; + case XK_Up: + case XK_KP_Up: + k = Up; + break; + case XK_Down: + case XK_KP_Down: + k = Down; + break; + case XK_Right: + case XK_KP_Right: + k = Right; + break; +// case XK_Page_Down: +// case XK_KP_Page_Down: +// k = Kpgdown; +// break; + case XK_End: + case XK_KP_End: + k = End; + break; +// case XK_Page_Up: +// case XK_KP_Page_Up: +// k = Kpgup; +// break; +// case XK_Insert: +// case XK_KP_Insert: +// k = Kins; +// break; + case XK_KP_Enter: + case XK_Return: + k = '\n'; + break; + case XK_Alt_L: + case XK_Alt_R: + k = Latin; + break; + case XK_Shift_L: + case XK_Shift_R: + case XK_Control_L: + case XK_Control_R: + case XK_Caps_Lock: + case XK_Shift_Lock: + + case XK_Meta_L: + case XK_Meta_R: + case XK_Super_L: + case XK_Super_R: + case XK_Hyper_L: + case XK_Hyper_R: + return; + default: /* not ISO-1 or tty control */ + if(k>0xff){ + k = keysym2ucs(k); /* supplied by X */ + if(k == -1) + return; + } + break; + } + } + + /* Compensate for servers that call a minus a hyphen */ + if(k == XK_hyphen) + k = XK_minus; + /* Do control mapping ourselves if translator doesn't */ + if(md & ControlMask) + k &= 0x9f; + if(0){ + if(k == '\t' && ind) + k = BackTab; + + if(md & Mod1Mask) + k = APP|(k&0xff); + } + if(k == NoSymbol) + return; + + gkbdputc(gkbdq, k); +} + +static void +xmouse(XEvent *e) +{ + int s, dbl; + XButtonEvent *be; + XMotionEvent *me; + XEvent motion; + int x, y, b; + static ulong lastb, lastt; + + if(putsnarf != assertsnarf){ + assertsnarf = putsnarf; + XSetSelectionOwner(xmcon, XA_PRIMARY, xdrawable, CurrentTime); + if(clipboard != None) + XSetSelectionOwner(xmcon, clipboard, xdrawable, CurrentTime); + XFlush(xmcon); + } + + dbl = 0; + switch(e->type){ + case ButtonPress: + be = (XButtonEvent *)e; + /* + * Fake message, just sent to make us announce snarf. + * Apparently state and button are 16 and 8 bits on + * the wire, since they are truncated by the time they + * get to us. + */ + if(be->send_event + && (~be->state&0xFFFF)==0 + && (~be->button&0xFF)==0) + return; + x = be->x; + y = be->y; + s = be->state; + if(be->button == lastb && be->time - lastt < DblTime) + dbl = 1; + lastb = be->button; + lastt = be->time; + switch(be->button){ + case 1: + s |= Button1Mask; + break; + case 2: + s |= Button2Mask; + break; + case 3: + s |= Button3Mask; + break; + case 4: + s |= Button4Mask; + break; + case 5: + s |= Button5Mask; + break; + } + break; + case ButtonRelease: + be = (XButtonEvent *)e; + x = be->x; + y = be->y; + s = be->state; + switch(be->button){ + case 1: + s &= ~Button1Mask; + break; + case 2: + s &= ~Button2Mask; + break; + case 3: + s &= ~Button3Mask; + break; + case 4: + s &= ~Button4Mask; + break; + case 5: + s &= ~Button5Mask; + break; + } + break; + case MotionNotify: + me = (XMotionEvent *) e; + + /* remove excess MotionNotify events from queue and keep last one */ + while(XCheckTypedWindowEvent(xmcon, xdrawable, MotionNotify, &motion) == True) + me = (XMotionEvent *) &motion; + + s = me->state; + x = me->x; + y = me->y; + break; + default: + return; + } + + b = 0; + if(s & Button1Mask) + b |= 1; + if(s & Button2Mask) + b |= 2; + if(s & Button3Mask) + b |= 4; + if(s & Button4Mask) + b |= 8; + if(s & Button5Mask) + b |= 16; + if(dbl) + b |= 1<<8; + + mousetrack(b, x, y, 0); +} + +#include "x11-keysym2ucs.c" + +/* + * Cut and paste. Just couldn't stand to make this simple... + */ + +enum{ + SnarfSize= 100*1024 +}; + +typedef struct Clip Clip; +struct Clip +{ + char buf[SnarfSize]; + QLock lk; +}; +Clip clip; + +#undef long /* sic */ +#undef ulong + +static char* +_xgetsnarf(XDisplay *xd) +{ + uchar *data, *xdata; + Atom clipboard, type, prop; + unsigned long len, lastlen, dummy; + int fmt, i; + XWindow w; + + qlock(&clip.lk); + /* + * Have we snarfed recently and the X server hasn't caught up? + */ + if(putsnarf != assertsnarf) + goto mine; + + /* + * Is there a primary selection (highlighted text in an xterm)? + */ + clipboard = XA_PRIMARY; + w = XGetSelectionOwner(xd, XA_PRIMARY); + if(w == xdrawable){ + mine: + data = (uchar*)strdup(clip.buf); + goto out; + } + + /* + * If not, is there a clipboard selection? + */ + if(w == None && clipboard != None){ + clipboard = clipboard; + w = XGetSelectionOwner(xd, clipboard); + if(w == xdrawable) + goto mine; + } + + /* + * If not, give up. + */ + if(w == None){ + data = nil; + goto out; + } + + /* + * We should be waiting for SelectionNotify here, but it might never + * come, and we have no way to time out. Instead, we will clear + * local property #1, request our buddy to fill it in for us, and poll + * until he's done or we get tired of waiting. + * + * We should try to go for utf8string instead of XA_STRING, + * but that would add to the polling. + */ + prop = 1; + XChangeProperty(xd, xdrawable, prop, XA_STRING, 8, PropModeReplace, (uchar*)"", 0); + XConvertSelection(xd, clipboard, XA_STRING, prop, xdrawable, CurrentTime); + XFlush(xd); + lastlen = 0; + for(i=0; i<10 || (lastlen!=0 && i<30); i++){ + osmillisleep(100); + XGetWindowProperty(xd, xdrawable, prop, 0, 0, 0, AnyPropertyType, + &type, &fmt, &dummy, &len, &data); + if(lastlen == len && len > 0) + break; + lastlen = len; + } + if(i == 10){ + data = nil; + goto out; + } + /* get the property */ + data = nil; + XGetWindowProperty(xd, xdrawable, prop, 0, SnarfSize/sizeof(unsigned long), 0, + AnyPropertyType, &type, &fmt, &len, &dummy, &xdata); + if((type != XA_STRING && type != utf8string) || len == 0){ + if(xdata) + XFree(xdata); + data = nil; + }else{ + if(xdata){ + data = (uchar*)strdup((char*)xdata); + XFree(xdata); + }else + data = nil; + } +out: + qunlock(&clip.lk); + return (char*)data; +} + +static void +_xputsnarf(XDisplay *xd, char *data) +{ + XButtonEvent e; + + if(strlen(data) >= SnarfSize) + return; + qlock(&clip.lk); + strcpy(clip.buf, data); + + /* leave note for mouse proc to assert selection ownership */ + putsnarf++; + + /* send mouse a fake event so snarf is announced */ + memset(&e, 0, sizeof e); + e.type = ButtonPress; + e.window = xdrawable; + e.state = ~0; + e.button = ~0; + XSendEvent(xd, xdrawable, True, ButtonPressMask, (XEvent*)&e); + XFlush(xd); + qunlock(&clip.lk); +} + +static void +xselect(XEvent *e, XDisplay *xd) +{ + char *name; + XEvent r; + XSelectionRequestEvent *xe; + Atom a[4]; + + if(e->xany.type != SelectionRequest) + return; + + memset(&r, 0, sizeof r); + xe = (XSelectionRequestEvent*)e; +if(0) iprint("xselect target=%d requestor=%d property=%d selection=%d\n", + xe->target, xe->requestor, xe->property, xe->selection); + r.xselection.property = xe->property; + if(xe->target == targets){ + a[0] = XA_STRING; + a[1] = utf8string; + a[2] = text; + a[3] = compoundtext; + + XChangeProperty(xd, xe->requestor, xe->property, xe->target, + 8, PropModeReplace, (uchar*)a, sizeof a); + }else if(xe->target == XA_STRING || xe->target == utf8string || xe->target == text || xe->target == compoundtext){ + /* if the target is STRING we're supposed to reply with Latin1 XXX */ + qlock(&clip.lk); + XChangeProperty(xd, xe->requestor, xe->property, xe->target, + 8, PropModeReplace, (uchar*)clip.buf, strlen(clip.buf)); + qunlock(&clip.lk); + }else{ + iprint("get %d\n", xe->target); + name = XGetAtomName(xd, xe->target); + if(name == nil) + iprint("XGetAtomName failed\n"); + else if(strcmp(name, "TIMESTAMP") != 0) + iprint("%s: cannot handle selection request for '%s' (%d)\n", argv0, name, (int)xe->target); + r.xselection.property = None; + } + + r.xselection.display = xe->display; + /* r.xselection.property filled above */ + r.xselection.target = xe->target; + r.xselection.type = SelectionNotify; + r.xselection.requestor = xe->requestor; + r.xselection.time = xe->time; + r.xselection.send_event = True; + r.xselection.selection = xe->selection; + XSendEvent(xd, xe->requestor, False, 0, &r); + XFlush(xd); +} + +char* +clipread(void) +{ + return _xgetsnarf(xsnarfcon); +} + +int +clipwrite(char *buf) +{ + _xputsnarf(xsnarfcon, buf); + return 0; +} diff --git a/emu/port/x11-keysym2ucs.c b/emu/port/x11-keysym2ucs.c new file mode 100644 index 00000000..b96d9624 --- /dev/null +++ b/emu/port/x11-keysym2ucs.c @@ -0,0 +1,857 @@ +/* $XFree86: xc/programs/xterm/keysym2ucs.c,v 1.5 2001/06/18 19:09:26 dickey Exp $ + * This module converts keysym values into the corresponding ISO 10646 + * (UCS, Unicode) values. + * + * The array keysymtab[] contains pairs of X11 keysym values for graphical + * characters and the corresponding Unicode value. The function + * keysym2ucs() maps a keysym onto a Unicode value using a binary search, + * therefore keysymtab[] must remain SORTED by keysym value. + * + * The keysym -> UTF-8 conversion will hopefully one day be provided + * by Xlib via XmbLookupString() and should ideally not have to be + * done in X applications. But we are not there yet. + * + * We allow to represent any UCS character in the range U-00000000 to + * U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff. + * This admittedly does not cover the entire 31-bit space of UCS, but + * it does cover all of the characters up to U-10FFFF, which can be + * represented by UTF-16, and more, and it is very unlikely that higher + * UCS codes will ever be assigned by ISO. So to get Unicode character + * U+ABCD you can directly use keysym 0x0100abcd. + * + * NOTE: The comments in the table below contain the actual character + * encoded in UTF-8, so for viewing and editing best use an editor in + * UTF-8 mode. + * + * Author: Markus G. Kuhn <mkuhn@acm.org>, University of Cambridge, April 2001 + * + * Special thanks to Richard Verhoeven <river@win.tue.nl> for preparing + * an initial draft of the mapping table. + * + * This software is in the public domain. Share and enjoy! + * + * AUTOMATICALLY GENERATED FILE, DO NOT EDIT !!! (unicode/convmap.pl) + */ + +#ifndef KEYSYM2UCS_INCLUDED + +#include "keysym2ucs.h" +#define VISIBLE /* */ + +#else + +#define VISIBLE static + +#endif + +static struct codepair { + unsigned short keysym; + unsigned short ucs; +} keysymtab[] = { + { 0x01a1, 0x0104 }, /* Aogonek Ą LATIN CAPITAL LETTER A WITH OGONEK */ + { 0x01a2, 0x02d8 }, /* breve ˘ BREVE */ + { 0x01a3, 0x0141 }, /* Lstroke Ł LATIN CAPITAL LETTER L WITH STROKE */ + { 0x01a5, 0x013d }, /* Lcaron Ľ LATIN CAPITAL LETTER L WITH CARON */ + { 0x01a6, 0x015a }, /* Sacute Ś LATIN CAPITAL LETTER S WITH ACUTE */ + { 0x01a9, 0x0160 }, /* Scaron Š LATIN CAPITAL LETTER S WITH CARON */ + { 0x01aa, 0x015e }, /* Scedilla Ş LATIN CAPITAL LETTER S WITH CEDILLA */ + { 0x01ab, 0x0164 }, /* Tcaron Ť LATIN CAPITAL LETTER T WITH CARON */ + { 0x01ac, 0x0179 }, /* Zacute Ź LATIN CAPITAL LETTER Z WITH ACUTE */ + { 0x01ae, 0x017d }, /* Zcaron Ž LATIN CAPITAL LETTER Z WITH CARON */ + { 0x01af, 0x017b }, /* Zabovedot Ż LATIN CAPITAL LETTER Z WITH DOT ABOVE */ + { 0x01b1, 0x0105 }, /* aogonek ą LATIN SMALL LETTER A WITH OGONEK */ + { 0x01b2, 0x02db }, /* ogonek ˛ OGONEK */ + { 0x01b3, 0x0142 }, /* lstroke ł LATIN SMALL LETTER L WITH STROKE */ + { 0x01b5, 0x013e }, /* lcaron ľ LATIN SMALL LETTER L WITH CARON */ + { 0x01b6, 0x015b }, /* sacute ś LATIN SMALL LETTER S WITH ACUTE */ + { 0x01b7, 0x02c7 }, /* caron ˇ CARON */ + { 0x01b9, 0x0161 }, /* scaron š LATIN SMALL LETTER S WITH CARON */ + { 0x01ba, 0x015f }, /* scedilla ş LATIN SMALL LETTER S WITH CEDILLA */ + { 0x01bb, 0x0165 }, /* tcaron ť LATIN SMALL LETTER T WITH CARON */ + { 0x01bc, 0x017a }, /* zacute ź LATIN SMALL LETTER Z WITH ACUTE */ + { 0x01bd, 0x02dd }, /* doubleacute ˝ DOUBLE ACUTE ACCENT */ + { 0x01be, 0x017e }, /* zcaron ž LATIN SMALL LETTER Z WITH CARON */ + { 0x01bf, 0x017c }, /* zabovedot ż LATIN SMALL LETTER Z WITH DOT ABOVE */ + { 0x01c0, 0x0154 }, /* Racute Ŕ LATIN CAPITAL LETTER R WITH ACUTE */ + { 0x01c3, 0x0102 }, /* Abreve Ă LATIN CAPITAL LETTER A WITH BREVE */ + { 0x01c5, 0x0139 }, /* Lacute Ĺ LATIN CAPITAL LETTER L WITH ACUTE */ + { 0x01c6, 0x0106 }, /* Cacute Ć LATIN CAPITAL LETTER C WITH ACUTE */ + { 0x01c8, 0x010c }, /* Ccaron Č LATIN CAPITAL LETTER C WITH CARON */ + { 0x01ca, 0x0118 }, /* Eogonek Ę LATIN CAPITAL LETTER E WITH OGONEK */ + { 0x01cc, 0x011a }, /* Ecaron Ě LATIN CAPITAL LETTER E WITH CARON */ + { 0x01cf, 0x010e }, /* Dcaron Ď LATIN CAPITAL LETTER D WITH CARON */ + { 0x01d0, 0x0110 }, /* Dstroke Đ LATIN CAPITAL LETTER D WITH STROKE */ + { 0x01d1, 0x0143 }, /* Nacute Ń LATIN CAPITAL LETTER N WITH ACUTE */ + { 0x01d2, 0x0147 }, /* Ncaron Ň LATIN CAPITAL LETTER N WITH CARON */ + { 0x01d5, 0x0150 }, /* Odoubleacute Ő LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */ + { 0x01d8, 0x0158 }, /* Rcaron Ř LATIN CAPITAL LETTER R WITH CARON */ + { 0x01d9, 0x016e }, /* Uring Ů LATIN CAPITAL LETTER U WITH RING ABOVE */ + { 0x01db, 0x0170 }, /* Udoubleacute Ű LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */ + { 0x01de, 0x0162 }, /* Tcedilla Ţ LATIN CAPITAL LETTER T WITH CEDILLA */ + { 0x01e0, 0x0155 }, /* racute ŕ LATIN SMALL LETTER R WITH ACUTE */ + { 0x01e3, 0x0103 }, /* abreve ă LATIN SMALL LETTER A WITH BREVE */ + { 0x01e5, 0x013a }, /* lacute ĺ LATIN SMALL LETTER L WITH ACUTE */ + { 0x01e6, 0x0107 }, /* cacute ć LATIN SMALL LETTER C WITH ACUTE */ + { 0x01e8, 0x010d }, /* ccaron č LATIN SMALL LETTER C WITH CARON */ + { 0x01ea, 0x0119 }, /* eogonek ę LATIN SMALL LETTER E WITH OGONEK */ + { 0x01ec, 0x011b }, /* ecaron ě LATIN SMALL LETTER E WITH CARON */ + { 0x01ef, 0x010f }, /* dcaron ď LATIN SMALL LETTER D WITH CARON */ + { 0x01f0, 0x0111 }, /* dstroke đ LATIN SMALL LETTER D WITH STROKE */ + { 0x01f1, 0x0144 }, /* nacute ń LATIN SMALL LETTER N WITH ACUTE */ + { 0x01f2, 0x0148 }, /* ncaron ň LATIN SMALL LETTER N WITH CARON */ + { 0x01f5, 0x0151 }, /* odoubleacute ő LATIN SMALL LETTER O WITH DOUBLE ACUTE */ + { 0x01f8, 0x0159 }, /* rcaron ř LATIN SMALL LETTER R WITH CARON */ + { 0x01f9, 0x016f }, /* uring ů LATIN SMALL LETTER U WITH RING ABOVE */ + { 0x01fb, 0x0171 }, /* udoubleacute ű LATIN SMALL LETTER U WITH DOUBLE ACUTE */ + { 0x01fe, 0x0163 }, /* tcedilla ţ LATIN SMALL LETTER T WITH CEDILLA */ + { 0x01ff, 0x02d9 }, /* abovedot ˙ DOT ABOVE */ + { 0x02a1, 0x0126 }, /* Hstroke Ħ LATIN CAPITAL LETTER H WITH STROKE */ + { 0x02a6, 0x0124 }, /* Hcircumflex Ĥ LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ + { 0x02a9, 0x0130 }, /* Iabovedot İ LATIN CAPITAL LETTER I WITH DOT ABOVE */ + { 0x02ab, 0x011e }, /* Gbreve Ğ LATIN CAPITAL LETTER G WITH BREVE */ + { 0x02ac, 0x0134 }, /* Jcircumflex Ĵ LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ + { 0x02b1, 0x0127 }, /* hstroke ħ LATIN SMALL LETTER H WITH STROKE */ + { 0x02b6, 0x0125 }, /* hcircumflex ĥ LATIN SMALL LETTER H WITH CIRCUMFLEX */ + { 0x02b9, 0x0131 }, /* idotless ı LATIN SMALL LETTER DOTLESS I */ + { 0x02bb, 0x011f }, /* gbreve ğ LATIN SMALL LETTER G WITH BREVE */ + { 0x02bc, 0x0135 }, /* jcircumflex ĵ LATIN SMALL LETTER J WITH CIRCUMFLEX */ + { 0x02c5, 0x010a }, /* Cabovedot Ċ LATIN CAPITAL LETTER C WITH DOT ABOVE */ + { 0x02c6, 0x0108 }, /* Ccircumflex Ĉ LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ + { 0x02d5, 0x0120 }, /* Gabovedot Ġ LATIN CAPITAL LETTER G WITH DOT ABOVE */ + { 0x02d8, 0x011c }, /* Gcircumflex Ĝ LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ + { 0x02dd, 0x016c }, /* Ubreve Ŭ LATIN CAPITAL LETTER U WITH BREVE */ + { 0x02de, 0x015c }, /* Scircumflex Ŝ LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ + { 0x02e5, 0x010b }, /* cabovedot ċ LATIN SMALL LETTER C WITH DOT ABOVE */ + { 0x02e6, 0x0109 }, /* ccircumflex ĉ LATIN SMALL LETTER C WITH CIRCUMFLEX */ + { 0x02f5, 0x0121 }, /* gabovedot ġ LATIN SMALL LETTER G WITH DOT ABOVE */ + { 0x02f8, 0x011d }, /* gcircumflex ĝ LATIN SMALL LETTER G WITH CIRCUMFLEX */ + { 0x02fd, 0x016d }, /* ubreve ŭ LATIN SMALL LETTER U WITH BREVE */ + { 0x02fe, 0x015d }, /* scircumflex ŝ LATIN SMALL LETTER S WITH CIRCUMFLEX */ + { 0x03a2, 0x0138 }, /* kra ĸ LATIN SMALL LETTER KRA */ + { 0x03a3, 0x0156 }, /* Rcedilla Ŗ LATIN CAPITAL LETTER R WITH CEDILLA */ + { 0x03a5, 0x0128 }, /* Itilde Ĩ LATIN CAPITAL LETTER I WITH TILDE */ + { 0x03a6, 0x013b }, /* Lcedilla Ļ LATIN CAPITAL LETTER L WITH CEDILLA */ + { 0x03aa, 0x0112 }, /* Emacron Ē LATIN CAPITAL LETTER E WITH MACRON */ + { 0x03ab, 0x0122 }, /* Gcedilla Ģ LATIN CAPITAL LETTER G WITH CEDILLA */ + { 0x03ac, 0x0166 }, /* Tslash Ŧ LATIN CAPITAL LETTER T WITH STROKE */ + { 0x03b3, 0x0157 }, /* rcedilla ŗ LATIN SMALL LETTER R WITH CEDILLA */ + { 0x03b5, 0x0129 }, /* itilde ĩ LATIN SMALL LETTER I WITH TILDE */ + { 0x03b6, 0x013c }, /* lcedilla ļ LATIN SMALL LETTER L WITH CEDILLA */ + { 0x03ba, 0x0113 }, /* emacron ē LATIN SMALL LETTER E WITH MACRON */ + { 0x03bb, 0x0123 }, /* gcedilla ģ LATIN SMALL LETTER G WITH CEDILLA */ + { 0x03bc, 0x0167 }, /* tslash ŧ LATIN SMALL LETTER T WITH STROKE */ + { 0x03bd, 0x014a }, /* ENG Ŋ LATIN CAPITAL LETTER ENG */ + { 0x03bf, 0x014b }, /* eng ŋ LATIN SMALL LETTER ENG */ + { 0x03c0, 0x0100 }, /* Amacron Ā LATIN CAPITAL LETTER A WITH MACRON */ + { 0x03c7, 0x012e }, /* Iogonek Į LATIN CAPITAL LETTER I WITH OGONEK */ + { 0x03cc, 0x0116 }, /* Eabovedot Ė LATIN CAPITAL LETTER E WITH DOT ABOVE */ + { 0x03cf, 0x012a }, /* Imacron Ī LATIN CAPITAL LETTER I WITH MACRON */ + { 0x03d1, 0x0145 }, /* Ncedilla Ņ LATIN CAPITAL LETTER N WITH CEDILLA */ + { 0x03d2, 0x014c }, /* Omacron Ō LATIN CAPITAL LETTER O WITH MACRON */ + { 0x03d3, 0x0136 }, /* Kcedilla Ķ LATIN CAPITAL LETTER K WITH CEDILLA */ + { 0x03d9, 0x0172 }, /* Uogonek Ų LATIN CAPITAL LETTER U WITH OGONEK */ + { 0x03dd, 0x0168 }, /* Utilde Ũ LATIN CAPITAL LETTER U WITH TILDE */ + { 0x03de, 0x016a }, /* Umacron Ū LATIN CAPITAL LETTER U WITH MACRON */ + { 0x03e0, 0x0101 }, /* amacron ā LATIN SMALL LETTER A WITH MACRON */ + { 0x03e7, 0x012f }, /* iogonek į LATIN SMALL LETTER I WITH OGONEK */ + { 0x03ec, 0x0117 }, /* eabovedot ė LATIN SMALL LETTER E WITH DOT ABOVE */ + { 0x03ef, 0x012b }, /* imacron ī LATIN SMALL LETTER I WITH MACRON */ + { 0x03f1, 0x0146 }, /* ncedilla ņ LATIN SMALL LETTER N WITH CEDILLA */ + { 0x03f2, 0x014d }, /* omacron ō LATIN SMALL LETTER O WITH MACRON */ + { 0x03f3, 0x0137 }, /* kcedilla ķ LATIN SMALL LETTER K WITH CEDILLA */ + { 0x03f9, 0x0173 }, /* uogonek ų LATIN SMALL LETTER U WITH OGONEK */ + { 0x03fd, 0x0169 }, /* utilde ũ LATIN SMALL LETTER U WITH TILDE */ + { 0x03fe, 0x016b }, /* umacron ū LATIN SMALL LETTER U WITH MACRON */ + { 0x047e, 0x203e }, /* overline ‾ OVERLINE */ + { 0x04a1, 0x3002 }, /* kana_fullstop 。 IDEOGRAPHIC FULL STOP */ + { 0x04a2, 0x300c }, /* kana_openingbracket 「 LEFT CORNER BRACKET */ + { 0x04a3, 0x300d }, /* kana_closingbracket 」 RIGHT CORNER BRACKET */ + { 0x04a4, 0x3001 }, /* kana_comma 、 IDEOGRAPHIC COMMA */ + { 0x04a5, 0x30fb }, /* kana_conjunctive ・ KATAKANA MIDDLE DOT */ + { 0x04a6, 0x30f2 }, /* kana_WO ヲ KATAKANA LETTER WO */ + { 0x04a7, 0x30a1 }, /* kana_a ァ KATAKANA LETTER SMALL A */ + { 0x04a8, 0x30a3 }, /* kana_i ィ KATAKANA LETTER SMALL I */ + { 0x04a9, 0x30a5 }, /* kana_u ゥ KATAKANA LETTER SMALL U */ + { 0x04aa, 0x30a7 }, /* kana_e ェ KATAKANA LETTER SMALL E */ + { 0x04ab, 0x30a9 }, /* kana_o ォ KATAKANA LETTER SMALL O */ + { 0x04ac, 0x30e3 }, /* kana_ya ャ KATAKANA LETTER SMALL YA */ + { 0x04ad, 0x30e5 }, /* kana_yu ュ KATAKANA LETTER SMALL YU */ + { 0x04ae, 0x30e7 }, /* kana_yo ョ KATAKANA LETTER SMALL YO */ + { 0x04af, 0x30c3 }, /* kana_tsu ッ KATAKANA LETTER SMALL TU */ + { 0x04b0, 0x30fc }, /* prolongedsound ー KATAKANA-HIRAGANA PROLONGED SOUND MARK */ + { 0x04b1, 0x30a2 }, /* kana_A ア KATAKANA LETTER A */ + { 0x04b2, 0x30a4 }, /* kana_I イ KATAKANA LETTER I */ + { 0x04b3, 0x30a6 }, /* kana_U ウ KATAKANA LETTER U */ + { 0x04b4, 0x30a8 }, /* kana_E エ KATAKANA LETTER E */ + { 0x04b5, 0x30aa }, /* kana_O オ KATAKANA LETTER O */ + { 0x04b6, 0x30ab }, /* kana_KA カ KATAKANA LETTER KA */ + { 0x04b7, 0x30ad }, /* kana_KI キ KATAKANA LETTER KI */ + { 0x04b8, 0x30af }, /* kana_KU ク KATAKANA LETTER KU */ + { 0x04b9, 0x30b1 }, /* kana_KE ケ KATAKANA LETTER KE */ + { 0x04ba, 0x30b3 }, /* kana_KO コ KATAKANA LETTER KO */ + { 0x04bb, 0x30b5 }, /* kana_SA サ KATAKANA LETTER SA */ + { 0x04bc, 0x30b7 }, /* kana_SHI シ KATAKANA LETTER SI */ + { 0x04bd, 0x30b9 }, /* kana_SU ス KATAKANA LETTER SU */ + { 0x04be, 0x30bb }, /* kana_SE セ KATAKANA LETTER SE */ + { 0x04bf, 0x30bd }, /* kana_SO ソ KATAKANA LETTER SO */ + { 0x04c0, 0x30bf }, /* kana_TA タ KATAKANA LETTER TA */ + { 0x04c1, 0x30c1 }, /* kana_CHI チ KATAKANA LETTER TI */ + { 0x04c2, 0x30c4 }, /* kana_TSU ツ KATAKANA LETTER TU */ + { 0x04c3, 0x30c6 }, /* kana_TE テ KATAKANA LETTER TE */ + { 0x04c4, 0x30c8 }, /* kana_TO ト KATAKANA LETTER TO */ + { 0x04c5, 0x30ca }, /* kana_NA ナ KATAKANA LETTER NA */ + { 0x04c6, 0x30cb }, /* kana_NI ニ KATAKANA LETTER NI */ + { 0x04c7, 0x30cc }, /* kana_NU ヌ KATAKANA LETTER NU */ + { 0x04c8, 0x30cd }, /* kana_NE ネ KATAKANA LETTER NE */ + { 0x04c9, 0x30ce }, /* kana_NO ノ KATAKANA LETTER NO */ + { 0x04ca, 0x30cf }, /* kana_HA ハ KATAKANA LETTER HA */ + { 0x04cb, 0x30d2 }, /* kana_HI ヒ KATAKANA LETTER HI */ + { 0x04cc, 0x30d5 }, /* kana_FU フ KATAKANA LETTER HU */ + { 0x04cd, 0x30d8 }, /* kana_HE ヘ KATAKANA LETTER HE */ + { 0x04ce, 0x30db }, /* kana_HO ホ KATAKANA LETTER HO */ + { 0x04cf, 0x30de }, /* kana_MA マ KATAKANA LETTER MA */ + { 0x04d0, 0x30df }, /* kana_MI ミ KATAKANA LETTER MI */ + { 0x04d1, 0x30e0 }, /* kana_MU ム KATAKANA LETTER MU */ + { 0x04d2, 0x30e1 }, /* kana_ME メ KATAKANA LETTER ME */ + { 0x04d3, 0x30e2 }, /* kana_MO モ KATAKANA LETTER MO */ + { 0x04d4, 0x30e4 }, /* kana_YA ヤ KATAKANA LETTER YA */ + { 0x04d5, 0x30e6 }, /* kana_YU ユ KATAKANA LETTER YU */ + { 0x04d6, 0x30e8 }, /* kana_YO ヨ KATAKANA LETTER YO */ + { 0x04d7, 0x30e9 }, /* kana_RA ラ KATAKANA LETTER RA */ + { 0x04d8, 0x30ea }, /* kana_RI リ KATAKANA LETTER RI */ + { 0x04d9, 0x30eb }, /* kana_RU ル KATAKANA LETTER RU */ + { 0x04da, 0x30ec }, /* kana_RE レ KATAKANA LETTER RE */ + { 0x04db, 0x30ed }, /* kana_RO ロ KATAKANA LETTER RO */ + { 0x04dc, 0x30ef }, /* kana_WA ワ KATAKANA LETTER WA */ + { 0x04dd, 0x30f3 }, /* kana_N ン KATAKANA LETTER N */ + { 0x04de, 0x309b }, /* voicedsound ゛ KATAKANA-HIRAGANA VOICED SOUND MARK */ + { 0x04df, 0x309c }, /* semivoicedsound ゜ KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */ + { 0x05ac, 0x060c }, /* Arabic_comma ، ARABIC COMMA */ + { 0x05bb, 0x061b }, /* Arabic_semicolon ؛ ARABIC SEMICOLON */ + { 0x05bf, 0x061f }, /* Arabic_question_mark ؟ ARABIC QUESTION MARK */ + { 0x05c1, 0x0621 }, /* Arabic_hamza ء ARABIC LETTER HAMZA */ + { 0x05c2, 0x0622 }, /* Arabic_maddaonalef آ ARABIC LETTER ALEF WITH MADDA ABOVE */ + { 0x05c3, 0x0623 }, /* Arabic_hamzaonalef أ ARABIC LETTER ALEF WITH HAMZA ABOVE */ + { 0x05c4, 0x0624 }, /* Arabic_hamzaonwaw ؤ ARABIC LETTER WAW WITH HAMZA ABOVE */ + { 0x05c5, 0x0625 }, /* Arabic_hamzaunderalef إ ARABIC LETTER ALEF WITH HAMZA BELOW */ + { 0x05c6, 0x0626 }, /* Arabic_hamzaonyeh ئ ARABIC LETTER YEH WITH HAMZA ABOVE */ + { 0x05c7, 0x0627 }, /* Arabic_alef ا ARABIC LETTER ALEF */ + { 0x05c8, 0x0628 }, /* Arabic_beh ب ARABIC LETTER BEH */ + { 0x05c9, 0x0629 }, /* Arabic_tehmarbuta ة ARABIC LETTER TEH MARBUTA */ + { 0x05ca, 0x062a }, /* Arabic_teh ت ARABIC LETTER TEH */ + { 0x05cb, 0x062b }, /* Arabic_theh ث ARABIC LETTER THEH */ + { 0x05cc, 0x062c }, /* Arabic_jeem ج ARABIC LETTER JEEM */ + { 0x05cd, 0x062d }, /* Arabic_hah ح ARABIC LETTER HAH */ + { 0x05ce, 0x062e }, /* Arabic_khah خ ARABIC LETTER KHAH */ + { 0x05cf, 0x062f }, /* Arabic_dal د ARABIC LETTER DAL */ + { 0x05d0, 0x0630 }, /* Arabic_thal ذ ARABIC LETTER THAL */ + { 0x05d1, 0x0631 }, /* Arabic_ra ر ARABIC LETTER REH */ + { 0x05d2, 0x0632 }, /* Arabic_zain ز ARABIC LETTER ZAIN */ + { 0x05d3, 0x0633 }, /* Arabic_seen س ARABIC LETTER SEEN */ + { 0x05d4, 0x0634 }, /* Arabic_sheen ش ARABIC LETTER SHEEN */ + { 0x05d5, 0x0635 }, /* Arabic_sad ص ARABIC LETTER SAD */ + { 0x05d6, 0x0636 }, /* Arabic_dad ض ARABIC LETTER DAD */ + { 0x05d7, 0x0637 }, /* Arabic_tah ط ARABIC LETTER TAH */ + { 0x05d8, 0x0638 }, /* Arabic_zah ظ ARABIC LETTER ZAH */ + { 0x05d9, 0x0639 }, /* Arabic_ain ع ARABIC LETTER AIN */ + { 0x05da, 0x063a }, /* Arabic_ghain غ ARABIC LETTER GHAIN */ + { 0x05e0, 0x0640 }, /* Arabic_tatweel ـ ARABIC TATWEEL */ + { 0x05e1, 0x0641 }, /* Arabic_feh ف ARABIC LETTER FEH */ + { 0x05e2, 0x0642 }, /* Arabic_qaf ق ARABIC LETTER QAF */ + { 0x05e3, 0x0643 }, /* Arabic_kaf ك ARABIC LETTER KAF */ + { 0x05e4, 0x0644 }, /* Arabic_lam ل ARABIC LETTER LAM */ + { 0x05e5, 0x0645 }, /* Arabic_meem م ARABIC LETTER MEEM */ + { 0x05e6, 0x0646 }, /* Arabic_noon ن ARABIC LETTER NOON */ + { 0x05e7, 0x0647 }, /* Arabic_ha ه ARABIC LETTER HEH */ + { 0x05e8, 0x0648 }, /* Arabic_waw و ARABIC LETTER WAW */ + { 0x05e9, 0x0649 }, /* Arabic_alefmaksura ى ARABIC LETTER ALEF MAKSURA */ + { 0x05ea, 0x064a }, /* Arabic_yeh ي ARABIC LETTER YEH */ + { 0x05eb, 0x064b }, /* Arabic_fathatan ً ARABIC FATHATAN */ + { 0x05ec, 0x064c }, /* Arabic_dammatan ٌ ARABIC DAMMATAN */ + { 0x05ed, 0x064d }, /* Arabic_kasratan ٍ ARABIC KASRATAN */ + { 0x05ee, 0x064e }, /* Arabic_fatha َ ARABIC FATHA */ + { 0x05ef, 0x064f }, /* Arabic_damma ُ ARABIC DAMMA */ + { 0x05f0, 0x0650 }, /* Arabic_kasra ِ ARABIC KASRA */ + { 0x05f1, 0x0651 }, /* Arabic_shadda ّ ARABIC SHADDA */ + { 0x05f2, 0x0652 }, /* Arabic_sukun ْ ARABIC SUKUN */ + { 0x06a1, 0x0452 }, /* Serbian_dje ђ CYRILLIC SMALL LETTER DJE */ + { 0x06a2, 0x0453 }, /* Macedonia_gje ѓ CYRILLIC SMALL LETTER GJE */ + { 0x06a3, 0x0451 }, /* Cyrillic_io ё CYRILLIC SMALL LETTER IO */ + { 0x06a4, 0x0454 }, /* Ukrainian_ie є CYRILLIC SMALL LETTER UKRAINIAN IE */ + { 0x06a5, 0x0455 }, /* Macedonia_dse ѕ CYRILLIC SMALL LETTER DZE */ + { 0x06a6, 0x0456 }, /* Ukrainian_i і CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */ + { 0x06a7, 0x0457 }, /* Ukrainian_yi ї CYRILLIC SMALL LETTER YI */ + { 0x06a8, 0x0458 }, /* Cyrillic_je ј CYRILLIC SMALL LETTER JE */ + { 0x06a9, 0x0459 }, /* Cyrillic_lje љ CYRILLIC SMALL LETTER LJE */ + { 0x06aa, 0x045a }, /* Cyrillic_nje њ CYRILLIC SMALL LETTER NJE */ + { 0x06ab, 0x045b }, /* Serbian_tshe ћ CYRILLIC SMALL LETTER TSHE */ + { 0x06ac, 0x045c }, /* Macedonia_kje ќ CYRILLIC SMALL LETTER KJE */ + { 0x06ae, 0x045e }, /* Byelorussian_shortu ў CYRILLIC SMALL LETTER SHORT U */ + { 0x06af, 0x045f }, /* Cyrillic_dzhe џ CYRILLIC SMALL LETTER DZHE */ + { 0x06b0, 0x2116 }, /* numerosign № NUMERO SIGN */ + { 0x06b1, 0x0402 }, /* Serbian_DJE Ђ CYRILLIC CAPITAL LETTER DJE */ + { 0x06b2, 0x0403 }, /* Macedonia_GJE Ѓ CYRILLIC CAPITAL LETTER GJE */ + { 0x06b3, 0x0401 }, /* Cyrillic_IO Ё CYRILLIC CAPITAL LETTER IO */ + { 0x06b4, 0x0404 }, /* Ukrainian_IE Є CYRILLIC CAPITAL LETTER UKRAINIAN IE */ + { 0x06b5, 0x0405 }, /* Macedonia_DSE Ѕ CYRILLIC CAPITAL LETTER DZE */ + { 0x06b6, 0x0406 }, /* Ukrainian_I І CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */ + { 0x06b7, 0x0407 }, /* Ukrainian_YI Ї CYRILLIC CAPITAL LETTER YI */ + { 0x06b8, 0x0408 }, /* Cyrillic_JE Ј CYRILLIC CAPITAL LETTER JE */ + { 0x06b9, 0x0409 }, /* Cyrillic_LJE Љ CYRILLIC CAPITAL LETTER LJE */ + { 0x06ba, 0x040a }, /* Cyrillic_NJE Њ CYRILLIC CAPITAL LETTER NJE */ + { 0x06bb, 0x040b }, /* Serbian_TSHE Ћ CYRILLIC CAPITAL LETTER TSHE */ + { 0x06bc, 0x040c }, /* Macedonia_KJE Ќ CYRILLIC CAPITAL LETTER KJE */ + { 0x06be, 0x040e }, /* Byelorussian_SHORTU Ў CYRILLIC CAPITAL LETTER SHORT U */ + { 0x06bf, 0x040f }, /* Cyrillic_DZHE Џ CYRILLIC CAPITAL LETTER DZHE */ + { 0x06c0, 0x044e }, /* Cyrillic_yu ю CYRILLIC SMALL LETTER YU */ + { 0x06c1, 0x0430 }, /* Cyrillic_a а CYRILLIC SMALL LETTER A */ + { 0x06c2, 0x0431 }, /* Cyrillic_be б CYRILLIC SMALL LETTER BE */ + { 0x06c3, 0x0446 }, /* Cyrillic_tse ц CYRILLIC SMALL LETTER TSE */ + { 0x06c4, 0x0434 }, /* Cyrillic_de д CYRILLIC SMALL LETTER DE */ + { 0x06c5, 0x0435 }, /* Cyrillic_ie е CYRILLIC SMALL LETTER IE */ + { 0x06c6, 0x0444 }, /* Cyrillic_ef ф CYRILLIC SMALL LETTER EF */ + { 0x06c7, 0x0433 }, /* Cyrillic_ghe г CYRILLIC SMALL LETTER GHE */ + { 0x06c8, 0x0445 }, /* Cyrillic_ha х CYRILLIC SMALL LETTER HA */ + { 0x06c9, 0x0438 }, /* Cyrillic_i и CYRILLIC SMALL LETTER I */ + { 0x06ca, 0x0439 }, /* Cyrillic_shorti й CYRILLIC SMALL LETTER SHORT I */ + { 0x06cb, 0x043a }, /* Cyrillic_ka к CYRILLIC SMALL LETTER KA */ + { 0x06cc, 0x043b }, /* Cyrillic_el л CYRILLIC SMALL LETTER EL */ + { 0x06cd, 0x043c }, /* Cyrillic_em м CYRILLIC SMALL LETTER EM */ + { 0x06ce, 0x043d }, /* Cyrillic_en н CYRILLIC SMALL LETTER EN */ + { 0x06cf, 0x043e }, /* Cyrillic_o о CYRILLIC SMALL LETTER O */ + { 0x06d0, 0x043f }, /* Cyrillic_pe п CYRILLIC SMALL LETTER PE */ + { 0x06d1, 0x044f }, /* Cyrillic_ya я CYRILLIC SMALL LETTER YA */ + { 0x06d2, 0x0440 }, /* Cyrillic_er р CYRILLIC SMALL LETTER ER */ + { 0x06d3, 0x0441 }, /* Cyrillic_es с CYRILLIC SMALL LETTER ES */ + { 0x06d4, 0x0442 }, /* Cyrillic_te т CYRILLIC SMALL LETTER TE */ + { 0x06d5, 0x0443 }, /* Cyrillic_u у CYRILLIC SMALL LETTER U */ + { 0x06d6, 0x0436 }, /* Cyrillic_zhe ж CYRILLIC SMALL LETTER ZHE */ + { 0x06d7, 0x0432 }, /* Cyrillic_ve в CYRILLIC SMALL LETTER VE */ + { 0x06d8, 0x044c }, /* Cyrillic_softsign ь CYRILLIC SMALL LETTER SOFT SIGN */ + { 0x06d9, 0x044b }, /* Cyrillic_yeru ы CYRILLIC SMALL LETTER YERU */ + { 0x06da, 0x0437 }, /* Cyrillic_ze з CYRILLIC SMALL LETTER ZE */ + { 0x06db, 0x0448 }, /* Cyrillic_sha ш CYRILLIC SMALL LETTER SHA */ + { 0x06dc, 0x044d }, /* Cyrillic_e э CYRILLIC SMALL LETTER E */ + { 0x06dd, 0x0449 }, /* Cyrillic_shcha щ CYRILLIC SMALL LETTER SHCHA */ + { 0x06de, 0x0447 }, /* Cyrillic_che ч CYRILLIC SMALL LETTER CHE */ + { 0x06df, 0x044a }, /* Cyrillic_hardsign ъ CYRILLIC SMALL LETTER HARD SIGN */ + { 0x06e0, 0x042e }, /* Cyrillic_YU Ю CYRILLIC CAPITAL LETTER YU */ + { 0x06e1, 0x0410 }, /* Cyrillic_A А CYRILLIC CAPITAL LETTER A */ + { 0x06e2, 0x0411 }, /* Cyrillic_BE Б CYRILLIC CAPITAL LETTER BE */ + { 0x06e3, 0x0426 }, /* Cyrillic_TSE Ц CYRILLIC CAPITAL LETTER TSE */ + { 0x06e4, 0x0414 }, /* Cyrillic_DE Д CYRILLIC CAPITAL LETTER DE */ + { 0x06e5, 0x0415 }, /* Cyrillic_IE Е CYRILLIC CAPITAL LETTER IE */ + { 0x06e6, 0x0424 }, /* Cyrillic_EF Ф CYRILLIC CAPITAL LETTER EF */ + { 0x06e7, 0x0413 }, /* Cyrillic_GHE Г CYRILLIC CAPITAL LETTER GHE */ + { 0x06e8, 0x0425 }, /* Cyrillic_HA Х CYRILLIC CAPITAL LETTER HA */ + { 0x06e9, 0x0418 }, /* Cyrillic_I И CYRILLIC CAPITAL LETTER I */ + { 0x06ea, 0x0419 }, /* Cyrillic_SHORTI Й CYRILLIC CAPITAL LETTER SHORT I */ + { 0x06eb, 0x041a }, /* Cyrillic_KA К CYRILLIC CAPITAL LETTER KA */ + { 0x06ec, 0x041b }, /* Cyrillic_EL Л CYRILLIC CAPITAL LETTER EL */ + { 0x06ed, 0x041c }, /* Cyrillic_EM М CYRILLIC CAPITAL LETTER EM */ + { 0x06ee, 0x041d }, /* Cyrillic_EN Н CYRILLIC CAPITAL LETTER EN */ + { 0x06ef, 0x041e }, /* Cyrillic_O О CYRILLIC CAPITAL LETTER O */ + { 0x06f0, 0x041f }, /* Cyrillic_PE П CYRILLIC CAPITAL LETTER PE */ + { 0x06f1, 0x042f }, /* Cyrillic_YA Я CYRILLIC CAPITAL LETTER YA */ + { 0x06f2, 0x0420 }, /* Cyrillic_ER Р CYRILLIC CAPITAL LETTER ER */ + { 0x06f3, 0x0421 }, /* Cyrillic_ES С CYRILLIC CAPITAL LETTER ES */ + { 0x06f4, 0x0422 }, /* Cyrillic_TE Т CYRILLIC CAPITAL LETTER TE */ + { 0x06f5, 0x0423 }, /* Cyrillic_U У CYRILLIC CAPITAL LETTER U */ + { 0x06f6, 0x0416 }, /* Cyrillic_ZHE Ж CYRILLIC CAPITAL LETTER ZHE */ + { 0x06f7, 0x0412 }, /* Cyrillic_VE В CYRILLIC CAPITAL LETTER VE */ + { 0x06f8, 0x042c }, /* Cyrillic_SOFTSIGN Ь CYRILLIC CAPITAL LETTER SOFT SIGN */ + { 0x06f9, 0x042b }, /* Cyrillic_YERU Ы CYRILLIC CAPITAL LETTER YERU */ + { 0x06fa, 0x0417 }, /* Cyrillic_ZE З CYRILLIC CAPITAL LETTER ZE */ + { 0x06fb, 0x0428 }, /* Cyrillic_SHA Ш CYRILLIC CAPITAL LETTER SHA */ + { 0x06fc, 0x042d }, /* Cyrillic_E Э CYRILLIC CAPITAL LETTER E */ + { 0x06fd, 0x0429 }, /* Cyrillic_SHCHA Щ CYRILLIC CAPITAL LETTER SHCHA */ + { 0x06fe, 0x0427 }, /* Cyrillic_CHE Ч CYRILLIC CAPITAL LETTER CHE */ + { 0x06ff, 0x042a }, /* Cyrillic_HARDSIGN Ъ CYRILLIC CAPITAL LETTER HARD SIGN */ + { 0x07a1, 0x0386 }, /* Greek_ALPHAaccent Ά GREEK CAPITAL LETTER ALPHA WITH TONOS */ + { 0x07a2, 0x0388 }, /* Greek_EPSILONaccent Έ GREEK CAPITAL LETTER EPSILON WITH TONOS */ + { 0x07a3, 0x0389 }, /* Greek_ETAaccent Ή GREEK CAPITAL LETTER ETA WITH TONOS */ + { 0x07a4, 0x038a }, /* Greek_IOTAaccent Ί GREEK CAPITAL LETTER IOTA WITH TONOS */ + { 0x07a5, 0x03aa }, /* Greek_IOTAdiaeresis Ϊ GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */ + { 0x07a7, 0x038c }, /* Greek_OMICRONaccent Ό GREEK CAPITAL LETTER OMICRON WITH TONOS */ + { 0x07a8, 0x038e }, /* Greek_UPSILONaccent Ύ GREEK CAPITAL LETTER UPSILON WITH TONOS */ + { 0x07a9, 0x03ab }, /* Greek_UPSILONdieresis Ϋ GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */ + { 0x07ab, 0x038f }, /* Greek_OMEGAaccent Ώ GREEK CAPITAL LETTER OMEGA WITH TONOS */ + { 0x07ae, 0x0385 }, /* Greek_accentdieresis ΅ GREEK DIALYTIKA TONOS */ + { 0x07af, 0x2015 }, /* Greek_horizbar ― HORIZONTAL BAR */ + { 0x07b1, 0x03ac }, /* Greek_alphaaccent ά GREEK SMALL LETTER ALPHA WITH TONOS */ + { 0x07b2, 0x03ad }, /* Greek_epsilonaccent έ GREEK SMALL LETTER EPSILON WITH TONOS */ + { 0x07b3, 0x03ae }, /* Greek_etaaccent ή GREEK SMALL LETTER ETA WITH TONOS */ + { 0x07b4, 0x03af }, /* Greek_iotaaccent ί GREEK SMALL LETTER IOTA WITH TONOS */ + { 0x07b5, 0x03ca }, /* Greek_iotadieresis ϊ GREEK SMALL LETTER IOTA WITH DIALYTIKA */ + { 0x07b6, 0x0390 }, /* Greek_iotaaccentdieresis ΐ GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */ + { 0x07b7, 0x03cc }, /* Greek_omicronaccent ό GREEK SMALL LETTER OMICRON WITH TONOS */ + { 0x07b8, 0x03cd }, /* Greek_upsilonaccent ύ GREEK SMALL LETTER UPSILON WITH TONOS */ + { 0x07b9, 0x03cb }, /* Greek_upsilondieresis ϋ GREEK SMALL LETTER UPSILON WITH DIALYTIKA */ + { 0x07ba, 0x03b0 }, /* Greek_upsilonaccentdieresis ΰ GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */ + { 0x07bb, 0x03ce }, /* Greek_omegaaccent ώ GREEK SMALL LETTER OMEGA WITH TONOS */ + { 0x07c1, 0x0391 }, /* Greek_ALPHA Α GREEK CAPITAL LETTER ALPHA */ + { 0x07c2, 0x0392 }, /* Greek_BETA Β GREEK CAPITAL LETTER BETA */ + { 0x07c3, 0x0393 }, /* Greek_GAMMA Γ GREEK CAPITAL LETTER GAMMA */ + { 0x07c4, 0x0394 }, /* Greek_DELTA Δ GREEK CAPITAL LETTER DELTA */ + { 0x07c5, 0x0395 }, /* Greek_EPSILON Ε GREEK CAPITAL LETTER EPSILON */ + { 0x07c6, 0x0396 }, /* Greek_ZETA Ζ GREEK CAPITAL LETTER ZETA */ + { 0x07c7, 0x0397 }, /* Greek_ETA Η GREEK CAPITAL LETTER ETA */ + { 0x07c8, 0x0398 }, /* Greek_THETA Θ GREEK CAPITAL LETTER THETA */ + { 0x07c9, 0x0399 }, /* Greek_IOTA Ι GREEK CAPITAL LETTER IOTA */ + { 0x07ca, 0x039a }, /* Greek_KAPPA Κ GREEK CAPITAL LETTER KAPPA */ + { 0x07cb, 0x039b }, /* Greek_LAMBDA Λ GREEK CAPITAL LETTER LAMDA */ + { 0x07cc, 0x039c }, /* Greek_MU Μ GREEK CAPITAL LETTER MU */ + { 0x07cd, 0x039d }, /* Greek_NU Ν GREEK CAPITAL LETTER NU */ + { 0x07ce, 0x039e }, /* Greek_XI Ξ GREEK CAPITAL LETTER XI */ + { 0x07cf, 0x039f }, /* Greek_OMICRON Ο GREEK CAPITAL LETTER OMICRON */ + { 0x07d0, 0x03a0 }, /* Greek_PI Π GREEK CAPITAL LETTER PI */ + { 0x07d1, 0x03a1 }, /* Greek_RHO Ρ GREEK CAPITAL LETTER RHO */ + { 0x07d2, 0x03a3 }, /* Greek_SIGMA Σ GREEK CAPITAL LETTER SIGMA */ + { 0x07d4, 0x03a4 }, /* Greek_TAU Τ GREEK CAPITAL LETTER TAU */ + { 0x07d5, 0x03a5 }, /* Greek_UPSILON Υ GREEK CAPITAL LETTER UPSILON */ + { 0x07d6, 0x03a6 }, /* Greek_PHI Φ GREEK CAPITAL LETTER PHI */ + { 0x07d7, 0x03a7 }, /* Greek_CHI Χ GREEK CAPITAL LETTER CHI */ + { 0x07d8, 0x03a8 }, /* Greek_PSI Ψ GREEK CAPITAL LETTER PSI */ + { 0x07d9, 0x03a9 }, /* Greek_OMEGA Ω GREEK CAPITAL LETTER OMEGA */ + { 0x07e1, 0x03b1 }, /* Greek_alpha α GREEK SMALL LETTER ALPHA */ + { 0x07e2, 0x03b2 }, /* Greek_beta β GREEK SMALL LETTER BETA */ + { 0x07e3, 0x03b3 }, /* Greek_gamma γ GREEK SMALL LETTER GAMMA */ + { 0x07e4, 0x03b4 }, /* Greek_delta δ GREEK SMALL LETTER DELTA */ + { 0x07e5, 0x03b5 }, /* Greek_epsilon ε GREEK SMALL LETTER EPSILON */ + { 0x07e6, 0x03b6 }, /* Greek_zeta ζ GREEK SMALL LETTER ZETA */ + { 0x07e7, 0x03b7 }, /* Greek_eta η GREEK SMALL LETTER ETA */ + { 0x07e8, 0x03b8 }, /* Greek_theta θ GREEK SMALL LETTER THETA */ + { 0x07e9, 0x03b9 }, /* Greek_iota ι GREEK SMALL LETTER IOTA */ + { 0x07ea, 0x03ba }, /* Greek_kappa κ GREEK SMALL LETTER KAPPA */ + { 0x07eb, 0x03bb }, /* Greek_lambda λ GREEK SMALL LETTER LAMDA */ + { 0x07ec, 0x03bc }, /* Greek_mu μ GREEK SMALL LETTER MU */ + { 0x07ed, 0x03bd }, /* Greek_nu ν GREEK SMALL LETTER NU */ + { 0x07ee, 0x03be }, /* Greek_xi ξ GREEK SMALL LETTER XI */ + { 0x07ef, 0x03bf }, /* Greek_omicron ο GREEK SMALL LETTER OMICRON */ + { 0x07f0, 0x03c0 }, /* Greek_pi π GREEK SMALL LETTER PI */ + { 0x07f1, 0x03c1 }, /* Greek_rho ρ GREEK SMALL LETTER RHO */ + { 0x07f2, 0x03c3 }, /* Greek_sigma σ GREEK SMALL LETTER SIGMA */ + { 0x07f3, 0x03c2 }, /* Greek_finalsmallsigma ς GREEK SMALL LETTER FINAL SIGMA */ + { 0x07f4, 0x03c4 }, /* Greek_tau τ GREEK SMALL LETTER TAU */ + { 0x07f5, 0x03c5 }, /* Greek_upsilon υ GREEK SMALL LETTER UPSILON */ + { 0x07f6, 0x03c6 }, /* Greek_phi φ GREEK SMALL LETTER PHI */ + { 0x07f7, 0x03c7 }, /* Greek_chi χ GREEK SMALL LETTER CHI */ + { 0x07f8, 0x03c8 }, /* Greek_psi ψ GREEK SMALL LETTER PSI */ + { 0x07f9, 0x03c9 }, /* Greek_omega ω GREEK SMALL LETTER OMEGA */ + { 0x08a1, 0x23b7 }, /* leftradical ⎷ ??? */ + { 0x08a2, 0x250c }, /* topleftradical ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */ + { 0x08a3, 0x2500 }, /* horizconnector ─ BOX DRAWINGS LIGHT HORIZONTAL */ + { 0x08a4, 0x2320 }, /* topintegral ⌠ TOP HALF INTEGRAL */ + { 0x08a5, 0x2321 }, /* botintegral ⌡ BOTTOM HALF INTEGRAL */ + { 0x08a6, 0x2502 }, /* vertconnector │ BOX DRAWINGS LIGHT VERTICAL */ + { 0x08a7, 0x23a1 }, /* topleftsqbracket ⎡ ??? */ + { 0x08a8, 0x23a3 }, /* botleftsqbracket ⎣ ??? */ + { 0x08a9, 0x23a4 }, /* toprightsqbracket ⎤ ??? */ + { 0x08aa, 0x23a6 }, /* botrightsqbracket ⎦ ??? */ + { 0x08ab, 0x239b }, /* topleftparens ⎛ ??? */ + { 0x08ac, 0x239d }, /* botleftparens ⎝ ??? */ + { 0x08ad, 0x239e }, /* toprightparens ⎞ ??? */ + { 0x08ae, 0x23a0 }, /* botrightparens ⎠ ??? */ + { 0x08af, 0x23a8 }, /* leftmiddlecurlybrace ⎨ ??? */ + { 0x08b0, 0x23ac }, /* rightmiddlecurlybrace ⎬ ??? */ +/* 0x08b1 topleftsummation ? ??? */ +/* 0x08b2 botleftsummation ? ??? */ +/* 0x08b3 topvertsummationconnector ? ??? */ +/* 0x08b4 botvertsummationconnector ? ??? */ +/* 0x08b5 toprightsummation ? ??? */ +/* 0x08b6 botrightsummation ? ??? */ +/* 0x08b7 rightmiddlesummation ? ??? */ + { 0x08bc, 0x2264 }, /* lessthanequal ≤ LESS-THAN OR EQUAL TO */ + { 0x08bd, 0x2260 }, /* notequal ≠ NOT EQUAL TO */ + { 0x08be, 0x2265 }, /* greaterthanequal ≥ GREATER-THAN OR EQUAL TO */ + { 0x08bf, 0x222b }, /* integral ∫ INTEGRAL */ + { 0x08c0, 0x2234 }, /* therefore ∴ THEREFORE */ + { 0x08c1, 0x221d }, /* variation ∝ PROPORTIONAL TO */ + { 0x08c2, 0x221e }, /* infinity ∞ INFINITY */ + { 0x08c5, 0x2207 }, /* nabla ∇ NABLA */ + { 0x08c8, 0x223c }, /* approximate ∼ TILDE OPERATOR */ + { 0x08c9, 0x2243 }, /* similarequal ≃ ASYMPTOTICALLY EQUAL TO */ + { 0x08cd, 0x21d4 }, /* ifonlyif ⇔ LEFT RIGHT DOUBLE ARROW */ + { 0x08ce, 0x21d2 }, /* implies ⇒ RIGHTWARDS DOUBLE ARROW */ + { 0x08cf, 0x2261 }, /* identical ≡ IDENTICAL TO */ + { 0x08d6, 0x221a }, /* radical √ SQUARE ROOT */ + { 0x08da, 0x2282 }, /* includedin ⊂ SUBSET OF */ + { 0x08db, 0x2283 }, /* includes ⊃ SUPERSET OF */ + { 0x08dc, 0x2229 }, /* intersection ∩ INTERSECTION */ + { 0x08dd, 0x222a }, /* union ∪ UNION */ + { 0x08de, 0x2227 }, /* logicaland ∧ LOGICAL AND */ + { 0x08df, 0x2228 }, /* logicalor ∨ LOGICAL OR */ + { 0x08ef, 0x2202 }, /* partialderivative ∂ PARTIAL DIFFERENTIAL */ + { 0x08f6, 0x0192 }, /* function ƒ LATIN SMALL LETTER F WITH HOOK */ + { 0x08fb, 0x2190 }, /* leftarrow ← LEFTWARDS ARROW */ + { 0x08fc, 0x2191 }, /* uparrow ↑ UPWARDS ARROW */ + { 0x08fd, 0x2192 }, /* rightarrow → RIGHTWARDS ARROW */ + { 0x08fe, 0x2193 }, /* downarrow ↓ DOWNWARDS ARROW */ +/* 0x09df blank ? ??? */ + { 0x09e0, 0x25c6 }, /* soliddiamond ◆ BLACK DIAMOND */ + { 0x09e1, 0x2592 }, /* checkerboard ▒ MEDIUM SHADE */ + { 0x09e2, 0x2409 }, /* ht ␉ SYMBOL FOR HORIZONTAL TABULATION */ + { 0x09e3, 0x240c }, /* ff ␌ SYMBOL FOR FORM FEED */ + { 0x09e4, 0x240d }, /* cr ␍ SYMBOL FOR CARRIAGE RETURN */ + { 0x09e5, 0x240a }, /* lf ␊ SYMBOL FOR LINE FEED */ + { 0x09e8, 0x2424 }, /* nl  SYMBOL FOR NEWLINE */ + { 0x09e9, 0x240b }, /* vt ␋ SYMBOL FOR VERTICAL TABULATION */ + { 0x09ea, 0x2518 }, /* lowrightcorner ┘ BOX DRAWINGS LIGHT UP AND LEFT */ + { 0x09eb, 0x2510 }, /* uprightcorner ┐ BOX DRAWINGS LIGHT DOWN AND LEFT */ + { 0x09ec, 0x250c }, /* upleftcorner ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */ + { 0x09ed, 0x2514 }, /* lowleftcorner └ BOX DRAWINGS LIGHT UP AND RIGHT */ + { 0x09ee, 0x253c }, /* crossinglines ┼ BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */ + { 0x09ef, 0x23ba }, /* horizlinescan1 ⎺ HORIZONTAL SCAN LINE-1 (Unicode 3.2 draft) */ + { 0x09f0, 0x23bb }, /* horizlinescan3 ⎻ HORIZONTAL SCAN LINE-3 (Unicode 3.2 draft) */ + { 0x09f1, 0x2500 }, /* horizlinescan5 ─ BOX DRAWINGS LIGHT HORIZONTAL */ + { 0x09f2, 0x23bc }, /* horizlinescan7 ⎼ HORIZONTAL SCAN LINE-7 (Unicode 3.2 draft) */ + { 0x09f3, 0x23bd }, /* horizlinescan9 ⎽ HORIZONTAL SCAN LINE-9 (Unicode 3.2 draft) */ + { 0x09f4, 0x251c }, /* leftt ├ BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ + { 0x09f5, 0x2524 }, /* rightt ┤ BOX DRAWINGS LIGHT VERTICAL AND LEFT */ + { 0x09f6, 0x2534 }, /* bott ┴ BOX DRAWINGS LIGHT UP AND HORIZONTAL */ + { 0x09f7, 0x252c }, /* topt ┬ BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */ + { 0x09f8, 0x2502 }, /* vertbar │ BOX DRAWINGS LIGHT VERTICAL */ + { 0x0aa1, 0x2003 }, /* emspace EM SPACE */ + { 0x0aa2, 0x2002 }, /* enspace EN SPACE */ + { 0x0aa3, 0x2004 }, /* em3space THREE-PER-EM SPACE */ + { 0x0aa4, 0x2005 }, /* em4space FOUR-PER-EM SPACE */ + { 0x0aa5, 0x2007 }, /* digitspace FIGURE SPACE */ + { 0x0aa6, 0x2008 }, /* punctspace PUNCTUATION SPACE */ + { 0x0aa7, 0x2009 }, /* thinspace THIN SPACE */ + { 0x0aa8, 0x200a }, /* hairspace HAIR SPACE */ + { 0x0aa9, 0x2014 }, /* emdash — EM DASH */ + { 0x0aaa, 0x2013 }, /* endash – EN DASH */ +/* 0x0aac signifblank ? ??? */ + { 0x0aae, 0x2026 }, /* ellipsis … HORIZONTAL ELLIPSIS */ + { 0x0aaf, 0x2025 }, /* doubbaselinedot ‥ TWO DOT LEADER */ + { 0x0ab0, 0x2153 }, /* onethird ⅓ VULGAR FRACTION ONE THIRD */ + { 0x0ab1, 0x2154 }, /* twothirds ⅔ VULGAR FRACTION TWO THIRDS */ + { 0x0ab2, 0x2155 }, /* onefifth ⅕ VULGAR FRACTION ONE FIFTH */ + { 0x0ab3, 0x2156 }, /* twofifths ⅖ VULGAR FRACTION TWO FIFTHS */ + { 0x0ab4, 0x2157 }, /* threefifths ⅗ VULGAR FRACTION THREE FIFTHS */ + { 0x0ab5, 0x2158 }, /* fourfifths ⅘ VULGAR FRACTION FOUR FIFTHS */ + { 0x0ab6, 0x2159 }, /* onesixth ⅙ VULGAR FRACTION ONE SIXTH */ + { 0x0ab7, 0x215a }, /* fivesixths ⅚ VULGAR FRACTION FIVE SIXTHS */ + { 0x0ab8, 0x2105 }, /* careof ℅ CARE OF */ + { 0x0abb, 0x2012 }, /* figdash ‒ FIGURE DASH */ + { 0x0abc, 0x2329 }, /* leftanglebracket 〈 LEFT-POINTING ANGLE BRACKET */ +/* 0x0abd decimalpoint ? ??? */ + { 0x0abe, 0x232a }, /* rightanglebracket 〉 RIGHT-POINTING ANGLE BRACKET */ +/* 0x0abf marker ? ??? */ + { 0x0ac3, 0x215b }, /* oneeighth ⅛ VULGAR FRACTION ONE EIGHTH */ + { 0x0ac4, 0x215c }, /* threeeighths ⅜ VULGAR FRACTION THREE EIGHTHS */ + { 0x0ac5, 0x215d }, /* fiveeighths ⅝ VULGAR FRACTION FIVE EIGHTHS */ + { 0x0ac6, 0x215e }, /* seveneighths ⅞ VULGAR FRACTION SEVEN EIGHTHS */ + { 0x0ac9, 0x2122 }, /* trademark ™ TRADE MARK SIGN */ + { 0x0aca, 0x2613 }, /* signaturemark ☓ SALTIRE */ +/* 0x0acb trademarkincircle ? ??? */ + { 0x0acc, 0x25c1 }, /* leftopentriangle ◁ WHITE LEFT-POINTING TRIANGLE */ + { 0x0acd, 0x25b7 }, /* rightopentriangle ▷ WHITE RIGHT-POINTING TRIANGLE */ + { 0x0ace, 0x25cb }, /* emopencircle ○ WHITE CIRCLE */ + { 0x0acf, 0x25af }, /* emopenrectangle ▯ WHITE VERTICAL RECTANGLE */ + { 0x0ad0, 0x2018 }, /* leftsinglequotemark ‘ LEFT SINGLE QUOTATION MARK */ + { 0x0ad1, 0x2019 }, /* rightsinglequotemark ’ RIGHT SINGLE QUOTATION MARK */ + { 0x0ad2, 0x201c }, /* leftdoublequotemark “ LEFT DOUBLE QUOTATION MARK */ + { 0x0ad3, 0x201d }, /* rightdoublequotemark ” RIGHT DOUBLE QUOTATION MARK */ + { 0x0ad4, 0x211e }, /* prescription ℞ PRESCRIPTION TAKE */ + { 0x0ad6, 0x2032 }, /* minutes ′ PRIME */ + { 0x0ad7, 0x2033 }, /* seconds ″ DOUBLE PRIME */ + { 0x0ad9, 0x271d }, /* latincross ✝ LATIN CROSS */ +/* 0x0ada hexagram ? ??? */ + { 0x0adb, 0x25ac }, /* filledrectbullet ▬ BLACK RECTANGLE */ + { 0x0adc, 0x25c0 }, /* filledlefttribullet ◀ BLACK LEFT-POINTING TRIANGLE */ + { 0x0add, 0x25b6 }, /* filledrighttribullet ▶ BLACK RIGHT-POINTING TRIANGLE */ + { 0x0ade, 0x25cf }, /* emfilledcircle ● BLACK CIRCLE */ + { 0x0adf, 0x25ae }, /* emfilledrect ▮ BLACK VERTICAL RECTANGLE */ + { 0x0ae0, 0x25e6 }, /* enopencircbullet ◦ WHITE BULLET */ + { 0x0ae1, 0x25ab }, /* enopensquarebullet ▫ WHITE SMALL SQUARE */ + { 0x0ae2, 0x25ad }, /* openrectbullet ▭ WHITE RECTANGLE */ + { 0x0ae3, 0x25b3 }, /* opentribulletup △ WHITE UP-POINTING TRIANGLE */ + { 0x0ae4, 0x25bd }, /* opentribulletdown ▽ WHITE DOWN-POINTING TRIANGLE */ + { 0x0ae5, 0x2606 }, /* openstar ☆ WHITE STAR */ + { 0x0ae6, 0x2022 }, /* enfilledcircbullet • BULLET */ + { 0x0ae7, 0x25aa }, /* enfilledsqbullet ▪ BLACK SMALL SQUARE */ + { 0x0ae8, 0x25b2 }, /* filledtribulletup ▲ BLACK UP-POINTING TRIANGLE */ + { 0x0ae9, 0x25bc }, /* filledtribulletdown ▼ BLACK DOWN-POINTING TRIANGLE */ + { 0x0aea, 0x261c }, /* leftpointer ☜ WHITE LEFT POINTING INDEX */ + { 0x0aeb, 0x261e }, /* rightpointer ☞ WHITE RIGHT POINTING INDEX */ + { 0x0aec, 0x2663 }, /* club ♣ BLACK CLUB SUIT */ + { 0x0aed, 0x2666 }, /* diamond ♦ BLACK DIAMOND SUIT */ + { 0x0aee, 0x2665 }, /* heart ♥ BLACK HEART SUIT */ + { 0x0af0, 0x2720 }, /* maltesecross ✠ MALTESE CROSS */ + { 0x0af1, 0x2020 }, /* dagger † DAGGER */ + { 0x0af2, 0x2021 }, /* doubledagger ‡ DOUBLE DAGGER */ + { 0x0af3, 0x2713 }, /* checkmark ✓ CHECK MARK */ + { 0x0af4, 0x2717 }, /* ballotcross ✗ BALLOT X */ + { 0x0af5, 0x266f }, /* musicalsharp ♯ MUSIC SHARP SIGN */ + { 0x0af6, 0x266d }, /* musicalflat ♭ MUSIC FLAT SIGN */ + { 0x0af7, 0x2642 }, /* malesymbol ♂ MALE SIGN */ + { 0x0af8, 0x2640 }, /* femalesymbol ♀ FEMALE SIGN */ + { 0x0af9, 0x260e }, /* telephone ☎ BLACK TELEPHONE */ + { 0x0afa, 0x2315 }, /* telephonerecorder ⌕ TELEPHONE RECORDER */ + { 0x0afb, 0x2117 }, /* phonographcopyright ℗ SOUND RECORDING COPYRIGHT */ + { 0x0afc, 0x2038 }, /* caret ‸ CARET */ + { 0x0afd, 0x201a }, /* singlelowquotemark ‚ SINGLE LOW-9 QUOTATION MARK */ + { 0x0afe, 0x201e }, /* doublelowquotemark „ DOUBLE LOW-9 QUOTATION MARK */ +/* 0x0aff cursor ? ??? */ + { 0x0ba3, 0x003c }, /* leftcaret < LESS-THAN SIGN */ + { 0x0ba6, 0x003e }, /* rightcaret > GREATER-THAN SIGN */ + { 0x0ba8, 0x2228 }, /* downcaret ∨ LOGICAL OR */ + { 0x0ba9, 0x2227 }, /* upcaret ∧ LOGICAL AND */ + { 0x0bc0, 0x00af }, /* overbar ¯ MACRON */ + { 0x0bc2, 0x22a5 }, /* downtack ⊥ UP TACK */ + { 0x0bc3, 0x2229 }, /* upshoe ∩ INTERSECTION */ + { 0x0bc4, 0x230a }, /* downstile ⌊ LEFT FLOOR */ + { 0x0bc6, 0x005f }, /* underbar _ LOW LINE */ + { 0x0bca, 0x2218 }, /* jot ∘ RING OPERATOR */ + { 0x0bcc, 0x2395 }, /* quad ⎕ APL FUNCTIONAL SYMBOL QUAD */ + { 0x0bce, 0x22a4 }, /* uptack ⊤ DOWN TACK */ + { 0x0bcf, 0x25cb }, /* circle ○ WHITE CIRCLE */ + { 0x0bd3, 0x2308 }, /* upstile ⌈ LEFT CEILING */ + { 0x0bd6, 0x222a }, /* downshoe ∪ UNION */ + { 0x0bd8, 0x2283 }, /* rightshoe ⊃ SUPERSET OF */ + { 0x0bda, 0x2282 }, /* leftshoe ⊂ SUBSET OF */ + { 0x0bdc, 0x22a2 }, /* lefttack ⊢ RIGHT TACK */ + { 0x0bfc, 0x22a3 }, /* righttack ⊣ LEFT TACK */ + { 0x0cdf, 0x2017 }, /* hebrew_doublelowline ‗ DOUBLE LOW LINE */ + { 0x0ce0, 0x05d0 }, /* hebrew_aleph א HEBREW LETTER ALEF */ + { 0x0ce1, 0x05d1 }, /* hebrew_bet ב HEBREW LETTER BET */ + { 0x0ce2, 0x05d2 }, /* hebrew_gimel ג HEBREW LETTER GIMEL */ + { 0x0ce3, 0x05d3 }, /* hebrew_dalet ד HEBREW LETTER DALET */ + { 0x0ce4, 0x05d4 }, /* hebrew_he ה HEBREW LETTER HE */ + { 0x0ce5, 0x05d5 }, /* hebrew_waw ו HEBREW LETTER VAV */ + { 0x0ce6, 0x05d6 }, /* hebrew_zain ז HEBREW LETTER ZAYIN */ + { 0x0ce7, 0x05d7 }, /* hebrew_chet ח HEBREW LETTER HET */ + { 0x0ce8, 0x05d8 }, /* hebrew_tet ט HEBREW LETTER TET */ + { 0x0ce9, 0x05d9 }, /* hebrew_yod י HEBREW LETTER YOD */ + { 0x0cea, 0x05da }, /* hebrew_finalkaph ך HEBREW LETTER FINAL KAF */ + { 0x0ceb, 0x05db }, /* hebrew_kaph כ HEBREW LETTER KAF */ + { 0x0cec, 0x05dc }, /* hebrew_lamed ל HEBREW LETTER LAMED */ + { 0x0ced, 0x05dd }, /* hebrew_finalmem ם HEBREW LETTER FINAL MEM */ + { 0x0cee, 0x05de }, /* hebrew_mem מ HEBREW LETTER MEM */ + { 0x0cef, 0x05df }, /* hebrew_finalnun ן HEBREW LETTER FINAL NUN */ + { 0x0cf0, 0x05e0 }, /* hebrew_nun נ HEBREW LETTER NUN */ + { 0x0cf1, 0x05e1 }, /* hebrew_samech ס HEBREW LETTER SAMEKH */ + { 0x0cf2, 0x05e2 }, /* hebrew_ayin ע HEBREW LETTER AYIN */ + { 0x0cf3, 0x05e3 }, /* hebrew_finalpe ף HEBREW LETTER FINAL PE */ + { 0x0cf4, 0x05e4 }, /* hebrew_pe פ HEBREW LETTER PE */ + { 0x0cf5, 0x05e5 }, /* hebrew_finalzade ץ HEBREW LETTER FINAL TSADI */ + { 0x0cf6, 0x05e6 }, /* hebrew_zade צ HEBREW LETTER TSADI */ + { 0x0cf7, 0x05e7 }, /* hebrew_qoph ק HEBREW LETTER QOF */ + { 0x0cf8, 0x05e8 }, /* hebrew_resh ר HEBREW LETTER RESH */ + { 0x0cf9, 0x05e9 }, /* hebrew_shin ש HEBREW LETTER SHIN */ + { 0x0cfa, 0x05ea }, /* hebrew_taw ת HEBREW LETTER TAV */ + { 0x0da1, 0x0e01 }, /* Thai_kokai ก THAI CHARACTER KO KAI */ + { 0x0da2, 0x0e02 }, /* Thai_khokhai ข THAI CHARACTER KHO KHAI */ + { 0x0da3, 0x0e03 }, /* Thai_khokhuat ฃ THAI CHARACTER KHO KHUAT */ + { 0x0da4, 0x0e04 }, /* Thai_khokhwai ค THAI CHARACTER KHO KHWAI */ + { 0x0da5, 0x0e05 }, /* Thai_khokhon ฅ THAI CHARACTER KHO KHON */ + { 0x0da6, 0x0e06 }, /* Thai_khorakhang ฆ THAI CHARACTER KHO RAKHANG */ + { 0x0da7, 0x0e07 }, /* Thai_ngongu ง THAI CHARACTER NGO NGU */ + { 0x0da8, 0x0e08 }, /* Thai_chochan จ THAI CHARACTER CHO CHAN */ + { 0x0da9, 0x0e09 }, /* Thai_choching ฉ THAI CHARACTER CHO CHING */ + { 0x0daa, 0x0e0a }, /* Thai_chochang ช THAI CHARACTER CHO CHANG */ + { 0x0dab, 0x0e0b }, /* Thai_soso ซ THAI CHARACTER SO SO */ + { 0x0dac, 0x0e0c }, /* Thai_chochoe ฌ THAI CHARACTER CHO CHOE */ + { 0x0dad, 0x0e0d }, /* Thai_yoying ญ THAI CHARACTER YO YING */ + { 0x0dae, 0x0e0e }, /* Thai_dochada ฎ THAI CHARACTER DO CHADA */ + { 0x0daf, 0x0e0f }, /* Thai_topatak ฏ THAI CHARACTER TO PATAK */ + { 0x0db0, 0x0e10 }, /* Thai_thothan ฐ THAI CHARACTER THO THAN */ + { 0x0db1, 0x0e11 }, /* Thai_thonangmontho ฑ THAI CHARACTER THO NANGMONTHO */ + { 0x0db2, 0x0e12 }, /* Thai_thophuthao ฒ THAI CHARACTER THO PHUTHAO */ + { 0x0db3, 0x0e13 }, /* Thai_nonen ณ THAI CHARACTER NO NEN */ + { 0x0db4, 0x0e14 }, /* Thai_dodek ด THAI CHARACTER DO DEK */ + { 0x0db5, 0x0e15 }, /* Thai_totao ต THAI CHARACTER TO TAO */ + { 0x0db6, 0x0e16 }, /* Thai_thothung ถ THAI CHARACTER THO THUNG */ + { 0x0db7, 0x0e17 }, /* Thai_thothahan ท THAI CHARACTER THO THAHAN */ + { 0x0db8, 0x0e18 }, /* Thai_thothong ธ THAI CHARACTER THO THONG */ + { 0x0db9, 0x0e19 }, /* Thai_nonu น THAI CHARACTER NO NU */ + { 0x0dba, 0x0e1a }, /* Thai_bobaimai บ THAI CHARACTER BO BAIMAI */ + { 0x0dbb, 0x0e1b }, /* Thai_popla ป THAI CHARACTER PO PLA */ + { 0x0dbc, 0x0e1c }, /* Thai_phophung ผ THAI CHARACTER PHO PHUNG */ + { 0x0dbd, 0x0e1d }, /* Thai_fofa ฝ THAI CHARACTER FO FA */ + { 0x0dbe, 0x0e1e }, /* Thai_phophan พ THAI CHARACTER PHO PHAN */ + { 0x0dbf, 0x0e1f }, /* Thai_fofan ฟ THAI CHARACTER FO FAN */ + { 0x0dc0, 0x0e20 }, /* Thai_phosamphao ภ THAI CHARACTER PHO SAMPHAO */ + { 0x0dc1, 0x0e21 }, /* Thai_moma ม THAI CHARACTER MO MA */ + { 0x0dc2, 0x0e22 }, /* Thai_yoyak ย THAI CHARACTER YO YAK */ + { 0x0dc3, 0x0e23 }, /* Thai_rorua ร THAI CHARACTER RO RUA */ + { 0x0dc4, 0x0e24 }, /* Thai_ru ฤ THAI CHARACTER RU */ + { 0x0dc5, 0x0e25 }, /* Thai_loling ล THAI CHARACTER LO LING */ + { 0x0dc6, 0x0e26 }, /* Thai_lu ฦ THAI CHARACTER LU */ + { 0x0dc7, 0x0e27 }, /* Thai_wowaen ว THAI CHARACTER WO WAEN */ + { 0x0dc8, 0x0e28 }, /* Thai_sosala ศ THAI CHARACTER SO SALA */ + { 0x0dc9, 0x0e29 }, /* Thai_sorusi ษ THAI CHARACTER SO RUSI */ + { 0x0dca, 0x0e2a }, /* Thai_sosua ส THAI CHARACTER SO SUA */ + { 0x0dcb, 0x0e2b }, /* Thai_hohip ห THAI CHARACTER HO HIP */ + { 0x0dcc, 0x0e2c }, /* Thai_lochula ฬ THAI CHARACTER LO CHULA */ + { 0x0dcd, 0x0e2d }, /* Thai_oang อ THAI CHARACTER O ANG */ + { 0x0dce, 0x0e2e }, /* Thai_honokhuk ฮ THAI CHARACTER HO NOKHUK */ + { 0x0dcf, 0x0e2f }, /* Thai_paiyannoi ฯ THAI CHARACTER PAIYANNOI */ + { 0x0dd0, 0x0e30 }, /* Thai_saraa ะ THAI CHARACTER SARA A */ + { 0x0dd1, 0x0e31 }, /* Thai_maihanakat ั THAI CHARACTER MAI HAN-AKAT */ + { 0x0dd2, 0x0e32 }, /* Thai_saraaa า THAI CHARACTER SARA AA */ + { 0x0dd3, 0x0e33 }, /* Thai_saraam ำ THAI CHARACTER SARA AM */ + { 0x0dd4, 0x0e34 }, /* Thai_sarai ิ THAI CHARACTER SARA I */ + { 0x0dd5, 0x0e35 }, /* Thai_saraii ี THAI CHARACTER SARA II */ + { 0x0dd6, 0x0e36 }, /* Thai_saraue ึ THAI CHARACTER SARA UE */ + { 0x0dd7, 0x0e37 }, /* Thai_sarauee ื THAI CHARACTER SARA UEE */ + { 0x0dd8, 0x0e38 }, /* Thai_sarau ุ THAI CHARACTER SARA U */ + { 0x0dd9, 0x0e39 }, /* Thai_sarauu ู THAI CHARACTER SARA UU */ + { 0x0dda, 0x0e3a }, /* Thai_phinthu ฺ THAI CHARACTER PHINTHU */ +/* 0x0dde Thai_maihanakat_maitho ? ??? */ + { 0x0ddf, 0x0e3f }, /* Thai_baht ฿ THAI CURRENCY SYMBOL BAHT */ + { 0x0de0, 0x0e40 }, /* Thai_sarae เ THAI CHARACTER SARA E */ + { 0x0de1, 0x0e41 }, /* Thai_saraae แ THAI CHARACTER SARA AE */ + { 0x0de2, 0x0e42 }, /* Thai_sarao โ THAI CHARACTER SARA O */ + { 0x0de3, 0x0e43 }, /* Thai_saraaimaimuan ใ THAI CHARACTER SARA AI MAIMUAN */ + { 0x0de4, 0x0e44 }, /* Thai_saraaimaimalai ไ THAI CHARACTER SARA AI MAIMALAI */ + { 0x0de5, 0x0e45 }, /* Thai_lakkhangyao ๅ THAI CHARACTER LAKKHANGYAO */ + { 0x0de6, 0x0e46 }, /* Thai_maiyamok ๆ THAI CHARACTER MAIYAMOK */ + { 0x0de7, 0x0e47 }, /* Thai_maitaikhu ็ THAI CHARACTER MAITAIKHU */ + { 0x0de8, 0x0e48 }, /* Thai_maiek ่ THAI CHARACTER MAI EK */ + { 0x0de9, 0x0e49 }, /* Thai_maitho ้ THAI CHARACTER MAI THO */ + { 0x0dea, 0x0e4a }, /* Thai_maitri ๊ THAI CHARACTER MAI TRI */ + { 0x0deb, 0x0e4b }, /* Thai_maichattawa ๋ THAI CHARACTER MAI CHATTAWA */ + { 0x0dec, 0x0e4c }, /* Thai_thanthakhat ์ THAI CHARACTER THANTHAKHAT */ + { 0x0ded, 0x0e4d }, /* Thai_nikhahit ํ THAI CHARACTER NIKHAHIT */ + { 0x0df0, 0x0e50 }, /* Thai_leksun ๐ THAI DIGIT ZERO */ + { 0x0df1, 0x0e51 }, /* Thai_leknung ๑ THAI DIGIT ONE */ + { 0x0df2, 0x0e52 }, /* Thai_leksong ๒ THAI DIGIT TWO */ + { 0x0df3, 0x0e53 }, /* Thai_leksam ๓ THAI DIGIT THREE */ + { 0x0df4, 0x0e54 }, /* Thai_leksi ๔ THAI DIGIT FOUR */ + { 0x0df5, 0x0e55 }, /* Thai_lekha ๕ THAI DIGIT FIVE */ + { 0x0df6, 0x0e56 }, /* Thai_lekhok ๖ THAI DIGIT SIX */ + { 0x0df7, 0x0e57 }, /* Thai_lekchet ๗ THAI DIGIT SEVEN */ + { 0x0df8, 0x0e58 }, /* Thai_lekpaet ๘ THAI DIGIT EIGHT */ + { 0x0df9, 0x0e59 }, /* Thai_lekkao ๙ THAI DIGIT NINE */ + { 0x0ea1, 0x3131 }, /* Hangul_Kiyeog ㄱ HANGUL LETTER KIYEOK */ + { 0x0ea2, 0x3132 }, /* Hangul_SsangKiyeog ㄲ HANGUL LETTER SSANGKIYEOK */ + { 0x0ea3, 0x3133 }, /* Hangul_KiyeogSios ㄳ HANGUL LETTER KIYEOK-SIOS */ + { 0x0ea4, 0x3134 }, /* Hangul_Nieun ㄴ HANGUL LETTER NIEUN */ + { 0x0ea5, 0x3135 }, /* Hangul_NieunJieuj ㄵ HANGUL LETTER NIEUN-CIEUC */ + { 0x0ea6, 0x3136 }, /* Hangul_NieunHieuh ㄶ HANGUL LETTER NIEUN-HIEUH */ + { 0x0ea7, 0x3137 }, /* Hangul_Dikeud ㄷ HANGUL LETTER TIKEUT */ + { 0x0ea8, 0x3138 }, /* Hangul_SsangDikeud ㄸ HANGUL LETTER SSANGTIKEUT */ + { 0x0ea9, 0x3139 }, /* Hangul_Rieul ㄹ HANGUL LETTER RIEUL */ + { 0x0eaa, 0x313a }, /* Hangul_RieulKiyeog ㄺ HANGUL LETTER RIEUL-KIYEOK */ + { 0x0eab, 0x313b }, /* Hangul_RieulMieum ㄻ HANGUL LETTER RIEUL-MIEUM */ + { 0x0eac, 0x313c }, /* Hangul_RieulPieub ㄼ HANGUL LETTER RIEUL-PIEUP */ + { 0x0ead, 0x313d }, /* Hangul_RieulSios ㄽ HANGUL LETTER RIEUL-SIOS */ + { 0x0eae, 0x313e }, /* Hangul_RieulTieut ㄾ HANGUL LETTER RIEUL-THIEUTH */ + { 0x0eaf, 0x313f }, /* Hangul_RieulPhieuf ㄿ HANGUL LETTER RIEUL-PHIEUPH */ + { 0x0eb0, 0x3140 }, /* Hangul_RieulHieuh ㅀ HANGUL LETTER RIEUL-HIEUH */ + { 0x0eb1, 0x3141 }, /* Hangul_Mieum ㅁ HANGUL LETTER MIEUM */ + { 0x0eb2, 0x3142 }, /* Hangul_Pieub ㅂ HANGUL LETTER PIEUP */ + { 0x0eb3, 0x3143 }, /* Hangul_SsangPieub ㅃ HANGUL LETTER SSANGPIEUP */ + { 0x0eb4, 0x3144 }, /* Hangul_PieubSios ㅄ HANGUL LETTER PIEUP-SIOS */ + { 0x0eb5, 0x3145 }, /* Hangul_Sios ㅅ HANGUL LETTER SIOS */ + { 0x0eb6, 0x3146 }, /* Hangul_SsangSios ㅆ HANGUL LETTER SSANGSIOS */ + { 0x0eb7, 0x3147 }, /* Hangul_Ieung ㅇ HANGUL LETTER IEUNG */ + { 0x0eb8, 0x3148 }, /* Hangul_Jieuj ㅈ HANGUL LETTER CIEUC */ + { 0x0eb9, 0x3149 }, /* Hangul_SsangJieuj ㅉ HANGUL LETTER SSANGCIEUC */ + { 0x0eba, 0x314a }, /* Hangul_Cieuc ㅊ HANGUL LETTER CHIEUCH */ + { 0x0ebb, 0x314b }, /* Hangul_Khieuq ㅋ HANGUL LETTER KHIEUKH */ + { 0x0ebc, 0x314c }, /* Hangul_Tieut ㅌ HANGUL LETTER THIEUTH */ + { 0x0ebd, 0x314d }, /* Hangul_Phieuf ㅍ HANGUL LETTER PHIEUPH */ + { 0x0ebe, 0x314e }, /* Hangul_Hieuh ㅎ HANGUL LETTER HIEUH */ + { 0x0ebf, 0x314f }, /* Hangul_A ㅏ HANGUL LETTER A */ + { 0x0ec0, 0x3150 }, /* Hangul_AE ㅐ HANGUL LETTER AE */ + { 0x0ec1, 0x3151 }, /* Hangul_YA ㅑ HANGUL LETTER YA */ + { 0x0ec2, 0x3152 }, /* Hangul_YAE ㅒ HANGUL LETTER YAE */ + { 0x0ec3, 0x3153 }, /* Hangul_EO ㅓ HANGUL LETTER EO */ + { 0x0ec4, 0x3154 }, /* Hangul_E ㅔ HANGUL LETTER E */ + { 0x0ec5, 0x3155 }, /* Hangul_YEO ㅕ HANGUL LETTER YEO */ + { 0x0ec6, 0x3156 }, /* Hangul_YE ㅖ HANGUL LETTER YE */ + { 0x0ec7, 0x3157 }, /* Hangul_O ㅗ HANGUL LETTER O */ + { 0x0ec8, 0x3158 }, /* Hangul_WA ㅘ HANGUL LETTER WA */ + { 0x0ec9, 0x3159 }, /* Hangul_WAE ㅙ HANGUL LETTER WAE */ + { 0x0eca, 0x315a }, /* Hangul_OE ㅚ HANGUL LETTER OE */ + { 0x0ecb, 0x315b }, /* Hangul_YO ㅛ HANGUL LETTER YO */ + { 0x0ecc, 0x315c }, /* Hangul_U ㅜ HANGUL LETTER U */ + { 0x0ecd, 0x315d }, /* Hangul_WEO ㅝ HANGUL LETTER WEO */ + { 0x0ece, 0x315e }, /* Hangul_WE ㅞ HANGUL LETTER WE */ + { 0x0ecf, 0x315f }, /* Hangul_WI ㅟ HANGUL LETTER WI */ + { 0x0ed0, 0x3160 }, /* Hangul_YU ㅠ HANGUL LETTER YU */ + { 0x0ed1, 0x3161 }, /* Hangul_EU ㅡ HANGUL LETTER EU */ + { 0x0ed2, 0x3162 }, /* Hangul_YI ㅢ HANGUL LETTER YI */ + { 0x0ed3, 0x3163 }, /* Hangul_I ㅣ HANGUL LETTER I */ + { 0x0ed4, 0x11a8 }, /* Hangul_J_Kiyeog ᆨ HANGUL JONGSEONG KIYEOK */ + { 0x0ed5, 0x11a9 }, /* Hangul_J_SsangKiyeog ᆩ HANGUL JONGSEONG SSANGKIYEOK */ + { 0x0ed6, 0x11aa }, /* Hangul_J_KiyeogSios ᆪ HANGUL JONGSEONG KIYEOK-SIOS */ + { 0x0ed7, 0x11ab }, /* Hangul_J_Nieun ᆫ HANGUL JONGSEONG NIEUN */ + { 0x0ed8, 0x11ac }, /* Hangul_J_NieunJieuj ᆬ HANGUL JONGSEONG NIEUN-CIEUC */ + { 0x0ed9, 0x11ad }, /* Hangul_J_NieunHieuh ᆭ HANGUL JONGSEONG NIEUN-HIEUH */ + { 0x0eda, 0x11ae }, /* Hangul_J_Dikeud ᆮ HANGUL JONGSEONG TIKEUT */ + { 0x0edb, 0x11af }, /* Hangul_J_Rieul ᆯ HANGUL JONGSEONG RIEUL */ + { 0x0edc, 0x11b0 }, /* Hangul_J_RieulKiyeog ᆰ HANGUL JONGSEONG RIEUL-KIYEOK */ + { 0x0edd, 0x11b1 }, /* Hangul_J_RieulMieum ᆱ HANGUL JONGSEONG RIEUL-MIEUM */ + { 0x0ede, 0x11b2 }, /* Hangul_J_RieulPieub ᆲ HANGUL JONGSEONG RIEUL-PIEUP */ + { 0x0edf, 0x11b3 }, /* Hangul_J_RieulSios ᆳ HANGUL JONGSEONG RIEUL-SIOS */ + { 0x0ee0, 0x11b4 }, /* Hangul_J_RieulTieut ᆴ HANGUL JONGSEONG RIEUL-THIEUTH */ + { 0x0ee1, 0x11b5 }, /* Hangul_J_RieulPhieuf ᆵ HANGUL JONGSEONG RIEUL-PHIEUPH */ + { 0x0ee2, 0x11b6 }, /* Hangul_J_RieulHieuh ᆶ HANGUL JONGSEONG RIEUL-HIEUH */ + { 0x0ee3, 0x11b7 }, /* Hangul_J_Mieum ᆷ HANGUL JONGSEONG MIEUM */ + { 0x0ee4, 0x11b8 }, /* Hangul_J_Pieub ᆸ HANGUL JONGSEONG PIEUP */ + { 0x0ee5, 0x11b9 }, /* Hangul_J_PieubSios ᆹ HANGUL JONGSEONG PIEUP-SIOS */ + { 0x0ee6, 0x11ba }, /* Hangul_J_Sios ᆺ HANGUL JONGSEONG SIOS */ + { 0x0ee7, 0x11bb }, /* Hangul_J_SsangSios ᆻ HANGUL JONGSEONG SSANGSIOS */ + { 0x0ee8, 0x11bc }, /* Hangul_J_Ieung ᆼ HANGUL JONGSEONG IEUNG */ + { 0x0ee9, 0x11bd }, /* Hangul_J_Jieuj ᆽ HANGUL JONGSEONG CIEUC */ + { 0x0eea, 0x11be }, /* Hangul_J_Cieuc ᆾ HANGUL JONGSEONG CHIEUCH */ + { 0x0eeb, 0x11bf }, /* Hangul_J_Khieuq ᆿ HANGUL JONGSEONG KHIEUKH */ + { 0x0eec, 0x11c0 }, /* Hangul_J_Tieut ᇀ HANGUL JONGSEONG THIEUTH */ + { 0x0eed, 0x11c1 }, /* Hangul_J_Phieuf ᇁ HANGUL JONGSEONG PHIEUPH */ + { 0x0eee, 0x11c2 }, /* Hangul_J_Hieuh ᇂ HANGUL JONGSEONG HIEUH */ + { 0x0eef, 0x316d }, /* Hangul_RieulYeorinHieuh ㅭ HANGUL LETTER RIEUL-YEORINHIEUH */ + { 0x0ef0, 0x3171 }, /* Hangul_SunkyeongeumMieum ㅱ HANGUL LETTER KAPYEOUNMIEUM */ + { 0x0ef1, 0x3178 }, /* Hangul_SunkyeongeumPieub ㅸ HANGUL LETTER KAPYEOUNPIEUP */ + { 0x0ef2, 0x317f }, /* Hangul_PanSios ㅿ HANGUL LETTER PANSIOS */ + { 0x0ef3, 0x3181 }, /* Hangul_KkogjiDalrinIeung ㆁ HANGUL LETTER YESIEUNG */ + { 0x0ef4, 0x3184 }, /* Hangul_SunkyeongeumPhieuf ㆄ HANGUL LETTER KAPYEOUNPHIEUPH */ + { 0x0ef5, 0x3186 }, /* Hangul_YeorinHieuh ㆆ HANGUL LETTER YEORINHIEUH */ + { 0x0ef6, 0x318d }, /* Hangul_AraeA ㆍ HANGUL LETTER ARAEA */ + { 0x0ef7, 0x318e }, /* Hangul_AraeAE ㆎ HANGUL LETTER ARAEAE */ + { 0x0ef8, 0x11eb }, /* Hangul_J_PanSios ᇫ HANGUL JONGSEONG PANSIOS */ + { 0x0ef9, 0x11f0 }, /* Hangul_J_KkogjiDalrinIeung ᇰ HANGUL JONGSEONG YESIEUNG */ + { 0x0efa, 0x11f9 }, /* Hangul_J_YeorinHieuh ᇹ HANGUL JONGSEONG YEORINHIEUH */ + { 0x0eff, 0x20a9 }, /* Korean_Won ₩ WON SIGN */ + { 0x13a4, 0x20ac }, /* Euro € EURO SIGN */ + { 0x13bc, 0x0152 }, /* OE Œ LATIN CAPITAL LIGATURE OE */ + { 0x13bd, 0x0153 }, /* oe œ LATIN SMALL LIGATURE OE */ + { 0x13be, 0x0178 }, /* Ydiaeresis Ÿ LATIN CAPITAL LETTER Y WITH DIAERESIS */ + { 0x20ac, 0x20ac }, /* EuroSign € EURO SIGN */ +}; + +VISIBLE +long keysym2ucs(KeySym keysym) +{ + int min = 0; + int max = sizeof(keysymtab) / sizeof(struct codepair) - 1; + int mid; + + /* first check for Latin-1 characters (1:1 mapping) */ + if ((keysym >= 0x0020 && keysym <= 0x007e) || + (keysym >= 0x00a0 && keysym <= 0x00ff)) + return keysym; + + /* also check for directly encoded 24-bit UCS characters */ + if ((keysym & 0xff000000) == 0x01000000) + return keysym & 0x00ffffff; + + /* binary search in table */ + while (max >= min) { + mid = (min + max) / 2; + if (keysymtab[mid].keysym < keysym) + min = mid + 1; + else if (keysymtab[mid].keysym > keysym) + max = mid - 1; + else { + /* found it */ + return keysymtab[mid].ucs; + } + } + + /* no matching Unicode value found */ + return -1; +} |
