#include "u.h" #include "lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "errno.h" #include "fcall.h" #include "devtab.h" typedef struct Mux Mux; typedef struct Con Con; typedef struct Dtq Dtq; enum { Qdir = 0, Qhead, Qclone, }; enum { Nmux = 20, }; struct Dtq { QLock rd; Rendez r; Lock listlk; Block *list; int ndelim; }; struct Con { int ref; char user[NAMELEN]; ulong perm; Dtq conq; }; struct Mux { Ref; char name[NAMELEN]; char user[NAMELEN]; ulong perm; int headopen; Dtq headq; Con connects[Nmux]; }; Mux *muxes; ulong muxreadq(Mux *m, Dtq*, char*, ulong); void muxwriteq(Dtq*, char*, long, int, int); #define NMUX(c) (((c->qid.path>>8)&0xffff)-1) #define NQID(m, c) (Qid){(m)<<8|(c)&0xff, 0} #define NCON(c) (c->qid.path&0xff) int muxgen(Chan *c, Dirtab *tab, int ntab, int s, Dir *dp) { Mux *m; int mux; Con *cm; char buf[10]; if(c->qid.path == CHDIR) { if(s >= conf.nmux) return -1; m = &muxes[s]; if(m->name[0] == '\0') return 0; devdir(c, (Qid){CHDIR|((s+1)<<8), 0}, m->name, 0, m->user, m->perm, dp); return 1; } if(s >= Nmux+2) return -1; mux = NMUX(c); m = &muxes[mux]; switch(s) { case Qhead: devdir(c, NQID(mux, Qhead), "head", m->headq.ndelim, m->user, m->perm, dp); break; case Qclone: devdir(c, NQID(mux, Qclone), "clone", 0, m->user, m->perm, dp); break; default: cm = &m->connects[s-Qclone]; if(cm->ref == 0) return 0; sprint(buf, "%d", s-Qclone); devdir(c, NQID(mux, Qclone+s), buf, cm->conq.ndelim, cm->user, cm->perm, dp); break; } return 1; } void muxinit(void) { } void muxreset(void) { muxes = ialloc(conf.nmux*sizeof(Mux), 0); } Chan * muxattach(char *spec) { Chan *c; c = devattach('m', spec); c->qid.path = CHDIR|Qdir; return c; } Chan * muxclone(Chan *c, Chan *nc) { int ncon; Mux *m; if(c->qid.path == CHDIR) return devclone(c, nc);; m = &muxes[NMUX(c)]; ncon = NCON(c); c = devclone(c, nc); switch(ncon) { case Qhead: incref(m); break; case Qclone: break; default: lock(m); m->connects[ncon].ref++; m->ref++; unlock(m); } return c; } int muxwalk(Chan *c, char *name) { if(strcmp(name, "..") == 0) { c->qid.path = CHDIR|Qdir; return 1; } return devwalk(c, name, 0, 0, muxgen); } void muxstat(Chan *c, char *db) { devstat(c, db, 0, 0, muxgen); } Chan * muxopen(Chan *c, int omode) { Mux *m; Con *cm, *e; if(c->qid.path & CHDIR) return devopen(c, omode, 0, 0, muxgen); m = &muxes[NMUX(c)]; switch(NCON(c)) { case Qhead: if(m->headopen) errors("server channel busy"); c = devopen(c, omode, 0, 0,muxgen); m->headopen = 1; incref(m); break; case Qclone: if(m->headopen == 0) errors("server shutdown"); c = devopen(c, omode, 0, 0, muxgen); lock(m); cm = m->connects; for(e = &cm[Nmux]; cm < e; cm++) if(cm->ref == 0) break; if(cm == e) { unlock(m); errors("all cannels busy"); } cm->ref++; m->ref++; unlock(m); strncpy(cm->user, u->p->user, NAMELEN); cm->perm = 0600; c->qid = NQID(NMUX(c), cm-m->connects); break; default: c = devopen(c, omode, 0, 0,muxgen); cm = &m->connects[NCON(c)]; cm->ref++; incref(m); break; } return c; } void muxcreate(Chan *c, char *name, int omode, ulong perm) { int n; Mux *m, *e; if(c->qid.path != CHDIR) error(Eperm); omode = openmode(omode); m = muxes; for(e = &m[conf.nmux]; m < e; m++) { if(m->ref == 0 && canlock(m)) { if(m->ref != 0) { unlock(m); continue; } m->ref++; break; } } if(m == e) errors("no multiplexors"); strncpy(m->name, name, NAMELEN); strncpy(m->user, u->p->user, NAMELEN); m->perm = perm&~CHDIR; unlock(m); n = m - muxes; c->qid = (Qid){CHDIR|(n+1)<<8, 0}; c->flag |= COPEN; c->mode = omode; } void muxremove(Chan *c) { Mux *m; if(c->qid.path == CHDIR || (c->qid.path&CHDIR) == 0) error(Eperm); m = &muxes[NMUX(c)]; if(strcmp(u->p->user, m->user) != 0) errors("not owner"); m->name[0] = '\0'; } void muxwstat(Chan *c, char *db) { Mux *m; Dir d; int nc; if(c->qid.path == CHDIR) error(Eperm); m = &muxes[NMUX(c)]; if(strcmp(u->p->user, m->user) != 0) errors("not owner"); convM2D(db, &d); d.mode &= 0777; if(c->qid.path&CHDIR) { strcpy(m->name, d.name); strcpy(m->user, d.uid); m->perm = d.mode; return; } nc = NCON(c); switch(nc) { case Qclone: error(Eperm); case Qhead: m->perm = d.mode; break; default: m->connects[nc].perm = d.mode; break; } } void muxclose(Chan *c) { Block *f1, *f2; Con *cm, *e; Mux *m; int nc; if(c->qid.path == CHDIR) return; m = &muxes[NMUX(c)]; nc = NCON(c); f1 = 0; f2 = 0; switch(nc) { case Qhead: m->headopen = 0; cm = m->connects; for(e = &cm[Nmux]; cm < e; cm++) if(cm->ref) wakeup(&cm->conq.r); lock(m); if(--m->ref == 0) { f1 = m->headq.list; m->headq.list = 0; } unlock(m); break; case Qclone: panic("muxclose"); default: lock(m); cm = &m->connects[nc]; if(--cm->ref == 0) { f1 = cm->conq.list; cm->conq.list = 0; } if(--m->ref == 0) { m->name[0] = '\0'; f2 = m->headq.list; m->headq.list = 0; } unlock(m); } if(f1) freeb(f1); if(f2) freeb(f2); } long muxread(Chan *c, void *va, long n, ulong offset) { Mux *m; Con *cm; int bread; if(c->qid.path & CHDIR) return devdirread(c, va, n, 0, 0, muxgen); m = &muxes[NMUX(c)]; switch(NCON(c)) { case Qhead: bread = muxreadq(m, &m->headq, va, n); break; case Qclone: error(Eperm); default: cm = &m->connects[NCON(c)]; bread = muxreadq(m, &cm->conq, va, n); break; } return bread; } Con * muxhdr(Mux *m, char *h) { Con *c; if(h[0] != Tmux) error(Ebadmsg); c = &m->connects[h[1]]; if(c < m->connects || c > &m->connects[Nmux]) error(Ebadmsg); if(c->ref == 0) return 0; return c; } long muxwrite(Chan *c, void *va, long n, ulong offset) { Mux *m; Con *cm; int muxid; Block *f, *bp; char *a, hdr[2]; if(c->qid.path & CHDIR) error(Eisdir); m = &muxes[NMUX(c)]; switch(NCON(c)) { case Qclone: error(Eperm); case Qhead: if(n < 2) error(Ebadmsg); a = (char*)va; memmove(hdr, a, sizeof(hdr)); cm = muxhdr(m, hdr); if(cm == 0) error(Ehungup); muxwriteq(&cm->conq, a+sizeof(hdr), n-sizeof(hdr), 0, 0); break; default: if(m->headopen == 0) error(Ehungup); muxid = NCON(c); muxwriteq(&m->headq, va, n, 1, muxid); break; } return n; } void muxwriteq(Dtq *q, char *va, long n, int addid, int muxid) { Block *head, *tail, *bp; ulong l; head = 0; SET(tail); if(waserror()) { if(head) freeb(head); nexterror(); } while(n) { bp = allocb(n); bp->type = M_DATA; l = bp->lim - bp->wptr; memmove(bp->wptr, va, l); /* Interruptable thru fault */ va += l; bp->wptr += l; n -= l; if(head == 0) head = bp; else tail->next = bp; tail = bp; } poperror(); tail->flags |= S_DELIM; lock(&q->listlk); for(tail = q->list; tail->next; tail = tail->next) ; tail->next = head; q->ndelim++; unlock(&q->listlk); } int nodata(Dtq *q) { int n; lock(&q->listlk); n = q->ndelim; unlock(&q->listlk); return n; } ulong muxreadq(Mux *m, Dtq *q, char *va, ulong n) { int l, nread, gotdelim; Block *bp; qlock(&q->rd); bp = 0; if(waserror()) { qunlock(&q->rd); lock(&q->listlk); if(bp) { bp->next = q->list; q->list = bp; } unlock(&q->listlk); nexterror(); } while(nodata(q)) sleep(&q->r, nodata, q); if(m->headopen == 0) errors("server shutdown"); nread = 0; while(n) { lock(&q->listlk); bp = q->list; q->list = bp->next; bp->next = 0; unlock(&q->listlk); l = BLEN(bp); if(n < l) n = l; memmove(va, bp->rptr, l); /* Interruptable thru fault */ va += l; bp->rptr += l; n -= l; gotdelim = bp->flags&S_DELIM; lock(&q->listlk); if(bp->rptr != bp->wptr) { bp->next = q->list; q->list = bp; } else if(gotdelim) q->ndelim--; unlock(&q->listlk); if(bp->rptr == bp->wptr) freeb(bp); if(gotdelim) break; } qunlock(&q->rd); return nread; }