ref: ab90a480eb5233abc4d7122739c24656dd42cc94
dir: /main.c/
#include <u.h> #include <libc.h> #include <fcall.h> #include <thread.h> #include <9p.h> #include <draw.h> #include <mouse.h> #include "guifs.h" #define Eexist "file does not exist" #define Enodir "not a directory" #define Eperm "permission denied" #define Eoffset "can't write to this file at non-zero offset" #define Ebadctl "bad ctl message" #define Einuse "file in use" QLock guilock = 0; char *username; enum { Qdir, Qclone, Qevent, Qtype, Qprops, Qprop, }; enum { Fclone, Fevent, Ftype, Fprops, Fmax, }; void fsattach(Req *); char *fswalk1(Fid *, char *, Qid *); char *fsclone(Fid *, Fid *); void fsopen(Req *); void fsstat(Req *); void fsread(Req *); void fswrite(Req *); void fsforker(void (*)(void*), void *, int); void fsdestroyfid(Fid *); Srv fs = { .attach = fsattach, .walk1 = fswalk1, .clone = fsclone, .open = fsopen, .stat = fsstat, .read = fsread, .write = fswrite, .destroyfid = fsdestroyfid, .forker = fsforker, }; GuiElement *root; #define QID_TYPE(q) ((q.path) & 0xFF) #define QID_PROP(q) (((q.path) >> 8) & 0xFF) void * emalloc(ulong size) { void *p = mallocz(size, 1); if(!p) sysfatal("malloc failed"); return p; } void * erealloc(void *p, ulong size) { p = realloc(p, size); if(!p) sysfatal("realloc failed"); return p; } Qid mkqid(int type) { static int id = 0; Qid q; q.vers = 0; q.path = (type & 0xFFFF) | (id << 16); id++; switch(type){ case Qdir: case Qprops: q.type = QTDIR; break; case Qclone: case Qevent: case Qtype: q.type = QTFILE; break; } return q; } Qid mkpropqid(int proptag) { return mkqid(Qprop | ((proptag & 0xFF) << 8)); } void settype(GuiElement *g, int type) { GuiSpec spec = guispecs[type]; int nprops = spec.nprops + nbaseprops; /* Allocate the props before locking the gui element, as some of the * allocations might cause this thread to block */ Prop *props = emalloc(sizeof(Prop) * nprops); for(int i = 0; i < nbaseprops; i++){ int tag = baseprops[i]; props[i].tag = tag; props[i].val = propspecs[tag].def(type, tag); props[i].qid = mkpropqid(tag); } for(int i = 0; i < spec.nprops; i++){ int tag = spec.proptags[i]; props[i+nbaseprops].tag = tag; props[i+nbaseprops].val = propspecs[tag].def(type, tag); props[i+nbaseprops].qid = mkpropqid(tag); } wlock(&g->lock); /* TODO: free old propvals */ free(g->props); g->type = type; g->nprops = nprops; g->props = props; wunlock(&g->lock); updategui(0); /* redraw everything */ } GuiElement * newgui(GuiElement *parent) { GuiElement *g = emalloc(sizeof(GuiElement)); g->parent = parent; g->qid = mkqid(Qdir); g->qclone = mkqid(Qclone); g->qevent = mkqid(Qevent); g->qtype = mkqid(Qtype); g->qprops = mkqid(Qprops); g->events = chancreate(sizeof(char *), 0); if(parent){ g->id = parent->nchildren; wlock(&parent->lock); parent->nchildren++; parent->children = erealloc(parent->children, parent->nchildren * sizeof(GuiElement *)); parent->children[g->id] = g; wunlock(&parent->lock); } settype(g, Gcontainer); return g; } GuiElement * findchild(GuiElement *g, char *name) { char *r; uvlong id = strtoull(name, &r, 10); if(*r != 0){ return nil; } rlock(&g->lock); GuiElement *child = (id < g->nchildren) ? g->children[id] : nil; runlock(&g->lock); return child; } void fsattach(Req *r) { if(root == nil){ GuiElement *g = newgui(nil); root = g; settype(g, Gcontainer); updategui(1); } r->fid->aux = root; r->fid->qid = root->qid; r->ofcall.qid = r->fid->qid; respond(r, nil); } char * fswalk1(Fid *fid, char *name, Qid *qid) { GuiElement *g = fid->aux; GuiElement *child; char *err = nil; switch(QID_TYPE(fid->qid)){ case Qdir: if(strcmp(name, "..") == 0){ if(g->parent == nil) // Root element *qid = g->qid; else{ fid->aux = g->parent; *qid = g->parent->qid; } }else if(strcmp(name, "clone") == 0){ rlock(&g->lock); if(guispecs[g->type].leafnode) err = Eexist; else *qid = g->qclone; runlock(&g->lock); }else if(strcmp(name, "event") == 0) *qid = g->qevent; else if(strcmp(name, "type") == 0) *qid = g->qtype; else if(strcmp(name, "props") == 0) *qid = g->qprops; else if(child = findchild(g, name)){ fid->aux = child; *qid = child->qid; }else err = Eexist; break; case Qprops: if(strcmp(name, "..") == 0) *qid = g->qid; else{ rlock(&g->lock); int ok = 0; for(int i = 0; i < g->nprops && ok == 0; i++){ PropSpec spec = propspecs[g->props[i].tag]; if(strcmp(name, spec.name) == 0){ *qid = g->props[i].qid; ok = 1; } } runlock(&g->lock); if(!ok) err = Eexist; } break; default: err = Enodir; break; } return err; } char * fsclone(Fid *old, Fid *new) { new->aux = old->aux; return nil; } void fsopen(Req *r) { GuiElement *g = r->fid->aux; char *err = nil; switch(QID_TYPE(r->fid->qid)){ case Qdir: case Qevent: case Qclone: case Qprops: if(r->ifcall.mode != OREAD){ err = Eperm; goto Lend; } break; } if(QID_TYPE(r->fid->qid) == Qclone){ /* Create a new child gui element */ GuiElement *child = newgui(g); assert(r->fid->qid.vers == child->id); /* Update qid version, so a read reports the correct child id */ assert(memcmp(&r->fid->qid, &g->qclone, sizeof(Qid)) == 0); g->qclone.vers++; r->fid->qid = g->qclone; r->ofcall.qid = g->qclone; } if(QID_TYPE(r->fid->qid) == Qevent){ wlock(&g->lock); if(g->listening){ err = Einuse; r->fid->aux = nil; }else g->listening = 1; wunlock(&g->lock); } Lend: respond(r, err); } void fsstat(Req *r) { r->d.qid = r->fid->qid; r->d.uid = estrdup9p(username); r->d.gid = estrdup9p(username); r->d.muid = estrdup9p(username); switch(QID_TYPE(r->fid->qid)){ case Qdir: r->d.name = estrdup9p("/"); r->d.mode = 0555|DMDIR; break; case Qprops: r->d.name = estrdup9p("/"); r->d.mode = 0555|DMDIR; break; } respond(r, nil); } int dirtreegen(int n, Dir *d, void *aux) { GuiElement *g = aux; d->uid = estrdup9p(username); d->gid = estrdup9p(username); d->muid = estrdup9p(username); rlock(&g->lock); if(guispecs[g->type].leafnode) n++; runlock(&g->lock); if(n < Fmax){ d->length = 0; switch(n){ case Fclone: d->mode = 0444; d->name = estrdup9p("clone"); d->qid = g->qclone; break; case Fevent: d->mode = 0444|DMEXCL; d->name = estrdup9p("event"); d->qid = g->qevent; break; case Ftype: d->mode = 0666; d->name = estrdup9p("type"); d->qid = g->qtype; break; case Fprops: d->mode = 0555|DMDIR; d->name = estrdup9p("props"); d->qid = g->qprops; break; } return 0; }else n -= Fmax; rlock(&g->lock); int done = n >= g->nchildren; if(!done){ GuiElement *child = g->children[n]; d->mode = DMDIR|0555; d->qid = child->qid; char buf[64]; snprint(buf, sizeof(buf), "%d", child->id); d->name = estrdup9p(buf); } runlock(&g->lock); return done ? -1 : 0; } int proptreegen(int n, Dir *d, void *aux) { GuiElement *g = aux; d->uid = estrdup9p(username); d->gid = estrdup9p(username); d->muid = estrdup9p(username); int done; rlock(&g->lock); done = n >= g->nprops; if(!done){ PropSpec spec = propspecs[g->props[n].tag]; d->mode = 0666; d->name = estrdup9p(spec.name); d->qid = g->props[n].qid; } runlock(&g->lock); return done ? -1 : 0; } void fsread(Req *r) { GuiElement *g = r->fid->aux; char buf[256]; switch(QID_TYPE(r->fid->qid)){ case Qdir: dirread9p(r, dirtreegen, g); break; case Qclone: snprint(buf, sizeof(buf), "%uld\n", r->fid->qid.vers-1); readstr(r, buf); break; case Qevent: { /* get all the messages we can, and add them to g->currentevents */ char *event; int mustrecv = 0; ulong currentsize; ulong eventsize; Lretry: srvrelease(&fs); if(mustrecv){ recv(g->events, &event); goto Lgotevent; } while(nbrecv(g->events, &event)){ Lgotevent: currentsize = g->currentevents ? strlen(g->currentevents) : 0; eventsize = strlen(event); wlock(&g->lock); g->currentevents = erealloc(g->currentevents, currentsize+eventsize+1); memcpy(g->currentevents+currentsize, event, eventsize); g->currentevents[currentsize+eventsize] = 0; wunlock(&g->lock); free(event); } rlock(&g->lock); if(g->currentevents == nil){ runlock(&g->lock); recv(g->events, &event); goto Lgotevent; }else{ srvacquire(&fs); readstr(r, g->currentevents); if(r->ofcall.count == 0){ runlock(&g->lock); mustrecv = 1; goto Lretry; } } runlock(&g->lock); } break; case Qtype: rlock(&g->lock); snprint(buf, sizeof(buf), "%s\n", guispecs[g->type].name); runlock(&g->lock); readstr(r, buf); break; case Qprops: dirread9p(r, proptreegen, g); break; case Qprop: { int tag = QID_PROP(r->fid->qid); PropSpec spec = propspecs[tag]; PropVal val = getprop(g, tag, 1); char *str = spec.print(val); readstr(r, str); free(str); } break; } respond(r, nil); } void fswrite(Req *r) { GuiElement *g = r->fid->aux; char *err = nil; switch(QID_TYPE(r->fid->qid)){ case Qtype: { char *buf = emalloc(r->ifcall.count + 1); buf[r->ifcall.count] = 0; memcpy(buf, r->ifcall.data, r->ifcall.count); int type; for(type = 0; type < Gmax; type++){ char *name = guispecs[type].name; int len = strlen(name); if(strncmp(buf, name, len) == 0 && allspace(buf+len)){ rlock(&g->lock); int canswitch = (g->nchildren == 0 || guispecs[type].leafnode == 0); runlock(&g->lock); if(canswitch) settype(g, type); else err = "old node has children, and new node type can't"; break; } } if(type == Gmax) err = "no such gui element type"; free(buf); } break; case Qprop: { int tag = QID_PROP(r->fid->qid); PropSpec spec = propspecs[tag]; PropVal val; char *buf = emalloc(r->ifcall.count + 1); buf[r->ifcall.count] = 0; memcpy(buf, r->ifcall.data, r->ifcall.count); err = spec.parse(buf, &val); if(err == nil) setprop(g, tag, val, 1); free(buf); } } respond(r, err); } void fsforker(void (*fn)(void*), void *arg, int rflag) { /* same as threadsrvforker, but stay in the same note group */ rflag &= ~RFNOTEG; procrfork(fn, arg, 32*1024, rflag); } void fsdestroyfid(Fid *fid) { GuiElement *g = fid->aux; if(g != nil && QID_TYPE(fid->qid) == Qevent){ wlock(&g->lock); g->listening = 0; free(g->currentevents); wunlock(&g->lock); } } void usage(void) { fprint(2, "usage: %s [-D] [-m mountpoint] [-s srvname] command\n", argv0); exits("usage"); } void threadmain(int argc, char **argv) { char *mtpt = "/mnt/gui"; char *srvname = nil; ARGBEGIN{ case 'D': chatty9p++; break; case 'm': mtpt = EARGF(usage()); break; case 's': srvname = EARGF(usage()); break; default: usage(); }ARGEND; if(argc == 0) usage(); username = getuser(); initgraphics(); threadpostmountsrv(&fs, srvname, mtpt, MREPL); int pid = fork(); switch(pid){ case 0: /* child process */ exec(argv[0], argv); sysfatal("exec: %r"); break; case -1: /* error */ sysfatal("fork: %r"); break; } /* parent process */ int wpid; do wpid = waitpid(); while(wpid != -1 && wpid != pid); postnote(PNGROUP, getpid(), "interrupt"); exits(nil); }