/* * I miss the Unix write(1) command, but I think * this is a better way to do it. */ #include #include #include #include #include <9p.h> #define min(A, B) ((A) < (B) ? (A) : (B)) #define STREQ(A, B) ((A)[0] == (B)[0] && strcmp(A, B) == 0) // Messages are consumed by readers either when they're // posted, or when the reader sends a Tread message. typedef struct Message Message; struct Message { Ref; char *msg; }; typedef struct Reader Reader; struct Reader { Reader *next; Channel *mq; Req *req; char *name; }; typedef struct Acl Acl; struct Acl { char **acl; int acllen; }; typedef struct Station Station; struct Station { Reader *readers; Acl *rallow; Acl *rdisallow; Acl *wallow; Acl *wdisallow; Acl *callow; Acl *cdisallow; }; static void *ezmalloc(ulong); static void *estrdup(char *); static void consume(Reader *); static void produce(Message *, Reader *); void wfsopen(Req *); void wfsread(Req *); void wfswrite(Req *); enum { POOLINCR = 20 }; Srv serv = { .open = wfsopen, .read = wfsread, .write= wfswrite }; Tree *tp; static void * ezmalloc(ulong size) { void *p; p = malloc(size); if (p == nil) sysfatal("malloc %lud bytes: %r\n", size); memset(p, 0, size); return(p); } static char * estrdup(char *str) { char *p; p = strdup(str); if (p == nil) sysfatal("estrdup: malloc failed: %r\n"); return(p); } static void produce(Message *mp, Reader *readers) { Reader *rp; for (rp = readers; rp != nil; rp = rp->next) incref(mp); for (rp = readers; rp != nil; rp = rp->next) sendp(rp->mq, mp); } void consume(void *reader) { Reader *rp; Message *mp; Req *req; long len; rp = reader; for ( ; ; ) { req = rp->req; if (req == nil) { yield(); continue; } mp = recvp(rp->mq); if (mp == nil) continue; len = min(strlen(mp->msg), req->ifcall.count); memcpy(req->ofcall.data, mp->msg, len); req->ofcall.count = len; if (decref(mp) == 0) free(mp); rp->req = nil; respond(req, nil); } threadexits(0); } Reader * addreader(File *file, char *user) { Station *sp; Reader *rp, *pp, *np; sp = file->aux; for (pp = nil, rp = sp->readers; rp != nil && strcmp(rp->name, user) < 0; pp = rp, rp = rp->next) ; if (rp != nil && STREQ(rp->name, user)) return(rp); np = ezmalloc(sizeof(*rp)); np->name = estrdup(user); np->req = nil; np->mq = chancreate(sizeof(Message *), 128); np->next = rp; if (pp == nil) { // At the beginning or empty. sp->readers = np; } else { pp->next = np; } proccreate(consume, np, 8192); return(np); } /* * Add ACL testing code. */ void wfsopen(Req *req) { if (!STREQ(req->fid->file->name, "clone") && !(req->fid->file->mode & DMDIR) && (req->ifcall.mode == OREAD) || req->ifcall.mode == ORDWR) addreader(req->fid->file, req->fid->uid); respond(req, nil); } void wfsread(Req *req) { File *file; Fid *fid; char *uid; Station *sp; Reader *rp; uid = req->fid->uid; file = req->fid->file; if (file == nil) sysfatal("fid->file == nil?"); if (STREQ(file->name, "clone")) { if (req->ifcall.offset > 0) { req->ofcall.count = 0; respond(req, nil); return; } file = createfile(tp->root, uid, uid, 0622, ezmalloc(sizeof(*sp))); if (file == nil) { respond(req, "user already exists"); return; } strcpy(req->ofcall.data, uid); req->ofcall.count = strlen(uid); respond(req, nil); return; } sp = file->aux; if (sp != nil) for (rp = sp->readers; rp != nil; rp = rp->next) if (STREQ(uid, rp->name)) { if (rp->req != nil) respond(rp->req, nil); rp->req = req; } } void wfswrite(Req *req) { Station *sp; Reader *rp; Message *mp; Fid *fid; int len; char date[32]; fid = req->fid; sp = fid->file->aux; if (sp->readers == nil) { respond(req, "no listeners"); return; } snprint(date, sizeof date, "%lld", (vlong)time(nil)); len = strlen(date) + 1 + strlen(fid->uid) + 1 + strlen(fid->file->name) + 1 + req->ifcall.count + 1; mp = ezmalloc(sizeof(*mp) + len); mp->msg = (char *)mp + sizeof(*mp); snprint(mp->msg, len, "%s:%s:%s:%.*s", date, fid->uid, fid->file->name, req->ifcall.count, req->ifcall.data); req->ofcall.count = req->ifcall.count; produce(mp, sp->readers); respond(req, nil); } void threadmain(int, char *[]) { File *f; char *user; rfork(RFNOTEG); user = getuser(); if (user == nil) user = "unknown"; tp = alloctree(user, user, DMDIR | 0664, nil); serv.tree = tp; f = createfile(tp->root, "clone", user, 0666, nil); if (f == nil) sysfatal("fcreate clone"); threadpostmountsrv(&serv, "writefs", "/mnt/write", MREPL); exits(0); }