diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/aux/consolefs.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/aux/consolefs.c')
-rwxr-xr-x | sys/src/cmd/aux/consolefs.c | 1289 |
1 files changed, 1289 insertions, 0 deletions
diff --git a/sys/src/cmd/aux/consolefs.c b/sys/src/cmd/aux/consolefs.c new file mode 100755 index 000000000..180431bd4 --- /dev/null +++ b/sys/src/cmd/aux/consolefs.c @@ -0,0 +1,1289 @@ +#include <u.h> +#include <libc.h> +#include <auth.h> +#include <fcall.h> +#include <bio.h> +#include <ndb.h> +#include <thread.h> + +/* + * This fs presents a 1 level file system. It contains + * up to three files per console (xxx and xxxctl and xxxstat) + */ + +typedef struct Console Console; +typedef struct Fid Fid; +typedef struct Request Request; +typedef struct Reqlist Reqlist; +typedef struct Fs Fs; + +enum +{ + /* last 5 bits of qid.path */ + Textern= 0, /* fake parent of top level */ + Ttopdir, /* top level directory */ + Qctl, + Qstat, + Qdata, + + Bufsize= 32*1024, /* chars buffered per reader */ + Maxcons= 64, /* maximum consoles */ + Nhash= 64, /* Fid hash buckets */ +}; + +#define TYPE(x) (((ulong)x.path) & 0xf) +#define CONS(x) ((((ulong)x.path) >> 4)&0xfff) +#define QID(c, x) (((c)<<4) | (x)) + +struct Request +{ + Request *next; + Fid *fid; + Fs *fs; + Fcall f; + uchar buf[1]; +}; + +struct Reqlist +{ + Lock; + Request *first; + Request *last; +}; + +struct Fid +{ + Lock; + Fid *next; /* hash list */ + Fid *cnext; /* list of Fid's on a console */ + int fid; + int ref; + + int attached; + int open; + char *user; + char mbuf[Bufsize]; /* message */ + int bufn; + int used; + Qid qid; + + Console *c; + + char buf[Bufsize]; + char *rp; + char *wp; + + Reqlist r; /* active read requests */ +}; + +struct Console +{ + Lock; + + char *name; + char *dev; + int speed; + int cronly; + int ondemand; /* open only on demand */ + int chat; /* chat consoles are special */ + + int pid; /* pid of reader */ + + int fd; + int cfd; + int sfd; + + Fid *flist; /* open fids to broadcast to */ +}; + +struct Fs +{ + Lock; + + int fd; /* to kernel mount point */ + int messagesize; + Fid *hash[Nhash]; + Console *cons[Maxcons]; + int ncons; +}; + +extern void console(Fs*, char*, char*, int, int, int); +extern Fs* fsmount(char*); + +extern void fsreader(void*); +extern void fsrun(void*); +extern Fid* fsgetfid(Fs*, int); +extern void fsputfid(Fs*, Fid*); +extern int fsdirgen(Fs*, Qid, int, Dir*, uchar*, int); +extern void fsreply(Fs*, Request*, char*); +extern void fskick(Fs*, Fid*); +extern int fsreopen(Fs*, Console*); + +extern void fsversion(Fs*, Request*, Fid*); +extern void fsflush(Fs*, Request*, Fid*); +extern void fsauth(Fs*, Request*, Fid*); +extern void fsattach(Fs*, Request*, Fid*); +extern void fswalk(Fs*, Request*, Fid*); +extern void fsclwalk(Fs*, Request*, Fid*); +extern void fsopen(Fs*, Request*, Fid*); +extern void fscreate(Fs*, Request*, Fid*); +extern void fsread(Fs*, Request*, Fid*); +extern void fswrite(Fs*, Request*, Fid*); +extern void fsclunk(Fs*, Request*, Fid*); +extern void fsremove(Fs*, Request*, Fid*); +extern void fsstat(Fs*, Request*, Fid*); +extern void fswstat(Fs*, Request*, Fid*); + + +void (*fcall[])(Fs*, Request*, Fid*) = +{ + [Tflush] fsflush, + [Tversion] fsversion, + [Tauth] fsauth, + [Tattach] fsattach, + [Twalk] fswalk, + [Topen] fsopen, + [Tcreate] fscreate, + [Tread] fsread, + [Twrite] fswrite, + [Tclunk] fsclunk, + [Tremove] fsremove, + [Tstat] fsstat, + [Twstat] fswstat +}; + +char Eperm[] = "permission denied"; +char Eexist[] = "file does not exist"; +char Enotdir[] = "not a directory"; +char Eisopen[] = "file already open"; +char Ebadcount[] = "bad read/write count"; +char Enofid[] = "no such fid"; + +char *consoledb = "/lib/ndb/consoledb"; +char *mntpt = "/mnt/consoles"; + +int messagesize = 8192+IOHDRSZ; + +void +fatal(char *fmt, ...) +{ + va_list arg; + char buf[1024]; + + write(2, "consolefs: ", 10); + va_start(arg, fmt); + vseprint(buf, buf+1024, fmt, arg); + va_end(arg); + write(2, buf, strlen(buf)); + write(2, "\n", 1); + threadexitsall(fmt); +} + + +void* +emalloc(uint n) +{ + void *p; + + p = malloc(n); + if(p == nil) + fatal("malloc failed: %r"); + memset(p, 0, n); + return p; +} + +int debug; +Ndb *db; + +/* + * any request that can get queued for a delayed reply + */ +Request* +allocreq(Fs *fs, int bufsize) +{ + Request *r; + + r = emalloc(sizeof(Request)+bufsize); + r->fs = fs; + r->next = nil; + return r; +} + +/* + * for maintaining lists of requests + */ +void +addreq(Reqlist *l, Request *r) +{ + lock(l); + if(l->first == nil) + l->first = r; + else + l->last->next = r; + l->last = r; + r->next = nil; + unlock(l); +} + +/* + * remove the first request from a list of requests + */ +Request* +remreq(Reqlist *l) +{ + Request *r; + + lock(l); + r = l->first; + if(r != nil) + l->first = r->next; + unlock(l); + return r; +} + +/* + * remove a request with the given tag from a list of requests + */ +Request* +remtag(Reqlist *l, int tag) +{ + Request *or, **ll; + + lock(l); + ll = &l->first; + for(or = *ll; or; or = or->next){ + if(or->f.tag == tag){ + *ll = or->next; + unlock(l); + return or; + } + ll = &or->next; + } + unlock(l); + return nil; +} + +Qid +parentqid(Qid q) +{ + if(q.type & QTDIR) + return (Qid){QID(0, Textern), 0, QTDIR}; + else + return (Qid){QID(0, Ttopdir), 0, QTDIR}; +} + +int +fsdirgen(Fs *fs, Qid parent, int i, Dir *d, uchar *buf, int nbuf) +{ + static char name[64]; + char *p; + int xcons; + + d->uid = d->gid = d->muid = "network"; + d->length = 0; + d->atime = time(nil); + d->mtime = d->atime; + d->type = 'C'; + d->dev = '0'; + + switch(TYPE(parent)){ + case Textern: + if(i != 0) + return -1; + p = "consoles"; + d->mode = DMDIR|0555; + d->qid.type = QTDIR; + d->qid.path = QID(0, Ttopdir); + d->qid.vers = 0; + break; + case Ttopdir: + xcons = i/3; + if(xcons >= fs->ncons) + return -1; + p = fs->cons[xcons]->name; + switch(i%3){ + case 0: + if(fs->cons[xcons]->cfd < 0) + return 0; + snprint(name, sizeof name, "%sctl", p); + p = name; + d->qid.type = QTFILE; + d->qid.path = QID(xcons, Qctl); + d->qid.vers = 0; + break; + case 1: + if(fs->cons[xcons]->sfd < 0) + return 0; + snprint(name, sizeof name, "%sstat", p); + p = name; + d->qid.type = QTFILE; + d->qid.path = QID(xcons, Qstat); + d->qid.vers = 0; + break; + case 2: + d->qid.type = QTFILE; + d->qid.path = QID(xcons, Qdata); + d->qid.vers = 0; + break; + } + d->mode = 0666; + break; + default: + return -1; + } + d->name = p; + if(buf != nil) + return convD2M(d, buf, nbuf); + return 1; +} + +/* + * mount the user interface and start a request processor + */ +Fs* +fsmount(char *mntpt) +{ + Fs *fs; + int pfd[2], srv; + char buf[32]; + int n; + static void *v[2]; + + fs = emalloc(sizeof(Fs)); + + if(pipe(pfd) < 0) + fatal("opening pipe: %r"); + + /* start up the file system process */ + v[0] = fs; + v[1] = pfd; + proccreate(fsrun, v, 16*1024); + + /* Typically mounted before /srv exists */ + if(access("#s/consoles", AEXIST) < 0){ + srv = create("#s/consoles", OWRITE, 0666); + if(srv < 0) + fatal("post: %r"); + + n = sprint(buf, "%d", pfd[1]); + if(write(srv, buf, n) < 0) + fatal("write srv: %r"); + + close(srv); + } + + mount(pfd[1], -1, mntpt, MBEFORE, ""); + close(pfd[1]); + return fs; +} + +/* + * reopen a console + */ +int +fsreopen(Fs* fs, Console *c) +{ + char buf[128]; + static void *v[2]; + + if(c->pid){ + if(postnote(PNPROC, c->pid, "reopen") != 0) + fprint(2, "postnote failed: %r\n"); + c->pid = 0; + } + + if(c->fd >= 0){ + close(c->fd); + close(c->cfd); + close(c->sfd); + c->cfd = -1; + c->fd = -1; + c->sfd = -1; + } + + if(c->flist == nil && c->ondemand) + return 0; + + c->fd = open(c->dev, ORDWR); + if(c->fd < 0) + return -1; + + snprint(buf, sizeof(buf), "%sctl", c->dev); + c->cfd = open(buf, ORDWR); + fprint(c->cfd, "b%d", c->speed); + + snprint(buf, sizeof(buf), "%sstat", c->dev); + c->sfd = open(buf, OREAD); + + v[0] = fs; + v[1] = c; + proccreate(fsreader, v, 16*1024); + + return 0; +} + +void +change(Fs *fs, Console *c, int doreopen, int speed, int cronly, int ondemand) +{ + lock(c); + + if(speed != c->speed){ + c->speed = speed; + doreopen = 1; + } + if(ondemand != c->ondemand){ + c->ondemand = ondemand; + doreopen = 1; + } + c->cronly = cronly; + if(doreopen) + fsreopen(fs, c); + + unlock(c); +} + +/* + * create a console interface + */ +void +console(Fs* fs, char *name, char *dev, int speed, int cronly, int ondemand) +{ + Console *c; + char *x; + int i, doreopen; + + if(fs->ncons >= Maxcons) + fatal("too many consoles, too little time"); + + doreopen = 0; + for(i = 0; i < fs->ncons; i++){ + c = fs->cons[i]; + if(strcmp(name, c->name) == 0){ + if(strcmp(dev, c->dev) != 0){ + /* new device */ + x = c->dev; + c->dev = strdup(dev); + free(x); + doreopen = 1; + } + change(fs, c, doreopen, speed, cronly, ondemand); + return; + } + } +#ifdef sapedoesntlikethis + /* + * The code below prevents this from working. I can't + * think of scenarios where the code below actually helps + * Sape + * + * console=borneo dev=/dev/eia1 + * speed=9600 + * openondemand=1 + * console=tottie dev=/dev/eia1 + * speed=115200 + * openondemand=1 + */ + for(i = 0; i < fs->ncons; i++){ + c = fs->cons[i]; + if(strcmp(dev, c->dev) == 0){ + /* at least a rename */ + x = c->name; + c->name = strdup(name); + free(x); + change(fs, c, doreopen, speed, cronly, ondemand); + return; + } + } +#endif + c = emalloc(sizeof(Console)); + fs->cons[fs->ncons] = c; + fs->ncons++; + c->name = strdup(name); + c->dev = strdup(dev); + if(strcmp(c->dev, "/dev/null") == 0) + c->chat = 1; + else + c->chat = 0; + c->fd = -1; + c->cfd = -1; + c->sfd = -1; + change(fs, c, 1, speed, cronly, ondemand); +} + +/* + * buffer data from console to a client. + * circular q with writer able to catch up to reader. + * the reader may miss data but always sees an in order sequence. + */ +void +fromconsole(Fid *f, char *p, int n) +{ + char *rp, *wp, *ep; + int pass; + + lock(f); + rp = f->rp; + wp = f->wp; + ep = f->buf + sizeof(f->buf); + pass = 0; + while(n--){ + *wp++ = *p++; + if(wp >= ep) + wp = f->buf; + if(rp == wp) + pass = 1; + } + f->wp = wp; + + /* we overtook the read pointer, push it up so readers always + * see the tail of what was written + */ + if(pass){ + wp++; + if(wp >= ep) + f->rp = f->buf; + else + f->rp = wp; + } + unlock(f); +} + +/* + * broadcast a list of members to all listeners + */ +void +bcastmembers(Fs *fs, Console *c, char *msg, Fid *f) +{ + int n; + Fid *fl; + char buf[512]; + + sprint(buf, "[%s%s", msg, f->user); + for(fl = c->flist; fl != nil && strlen(buf) + 64 < sizeof(buf); fl = fl->cnext){ + if(f == fl) + continue; + strcat(buf, ", "); + strcat(buf, fl->user); + } + strcat(buf, "]\n"); + + n = strlen(buf); + for(fl = c->flist; fl; fl = fl->cnext){ + fromconsole(fl, buf, n); + fskick(fs, fl); + } +} + +void +handler(void*, char *msg) +{ + if(strstr(msg, "reopen") != nil || + strstr(msg, "write on closed pipe") != nil) + noted(NCONT); + noted(NDFLT); +} + +/* + * a process to read console output and broadcast it (one per console) + */ +void +fsreader(void *v) +{ + int n; + Fid *fl; + char buf[1024]; + Fs *fs; + Console *c; + void **a; + + a = v; + fs = a[0]; + c = a[1]; + c->pid = getpid(); + notify(handler); + if(c->chat) + threadexits(nil); + for(;;){ + n = read(c->fd, buf, sizeof(buf)); + if(n < 0) + break; + lock(c); + for(fl = c->flist; fl; fl = fl->cnext){ + fromconsole(fl, buf, n); + fskick(fs, fl); + } + unlock(c); + } +} + +void +readdb(Fs *fs) +{ + Ndbtuple *t, *nt; + char *dev, *cons; + int cronly, speed, ondemand; + + ndbreopen(db); + + /* start a listener for each console */ + for(;;){ + t = ndbparse(db); + if(t == nil) + break; + dev = nil; + cons = nil; + speed = 9600; + cronly = 0; + ondemand = 0; + for(nt = t; nt; nt = nt->entry){ + if(strcmp(nt->attr, "console") == 0) + cons = nt->val; + else if(strcmp(nt->attr, "dev") == 0) + dev = nt->val; + else if(strcmp(nt->attr, "speed") == 0) + speed = atoi(nt->val); + else if(strcmp(nt->attr, "cronly") == 0) + cronly = 1; + else if(strcmp(nt->attr, "openondemand") == 0) + ondemand = 1; + } + if(dev != nil && cons != nil) + console(fs, cons, dev, speed, cronly, ondemand); + ndbfree(t); + } +} + +int dbmtime; + +/* + * a request processor (one per Fs) + */ +void +fsrun(void *v) +{ + int n, t; + Request *r; + Fid *f; + Dir *d; + void **a = v; + Fs* fs; + int *pfd; + + fs = a[0]; + pfd = a[1]; + fs->fd = pfd[0]; + notify(handler); + for(;;){ + d = dirstat(consoledb); + if(d != nil && d->mtime != dbmtime){ + dbmtime = d->mtime; + readdb(fs); + } + free(d); + r = allocreq(fs, messagesize); + n = read9pmsg(fs->fd, r->buf, messagesize); + if(n <= 0) + fatal("unmounted"); + + if(convM2S(r->buf, n, &r->f) == 0){ + fprint(2, "can't convert %ux %ux %ux\n", r->buf[0], + r->buf[1], r->buf[2]); + free(r); + continue; + } + + + f = fsgetfid(fs, r->f.fid); + r->fid = f; + if(debug) + fprint(2, "%F path %llux\n", &r->f, f->qid.path); + + t = r->f.type; + r->f.type++; + (*fcall[t])(fs, r, f); + } +} + +Fid* +fsgetfid(Fs *fs, int fid) +{ + Fid *f, *nf; + + lock(fs); + for(f = fs->hash[fid%Nhash]; f; f = f->next){ + if(f->fid == fid){ + f->ref++; + unlock(fs); + return f; + } + } + + nf = emalloc(sizeof(Fid)); + nf->next = fs->hash[fid%Nhash]; + fs->hash[fid%Nhash] = nf; + nf->fid = fid; + nf->ref = 1; + nf->wp = nf->buf; + nf->rp = nf->wp; + unlock(fs); + return nf; +} + +void +fsputfid(Fs *fs, Fid *f) +{ + Fid **l, *nf; + + lock(fs); + if(--f->ref > 0){ + unlock(fs); + return; + } + for(l = &fs->hash[f->fid%Nhash]; nf = *l; l = &nf->next) + if(nf == f){ + *l = f->next; + break; + } + unlock(fs); + free(f->user); + free(f); +} + +void +fsauth(Fs *fs, Request *r, Fid*) +{ + fsreply(fs, r, "consolefs: authentication not required"); +} + +void +fsversion(Fs *fs, Request *r, Fid*) +{ + + if(r->f.msize < 256){ + fsreply(fs, r, "message size too small"); + return; + } + messagesize = r->f.msize; + if(messagesize > 8192+IOHDRSZ) + messagesize = 8192+IOHDRSZ; + r->f.msize = messagesize; + if(strncmp(r->f.version, "9P2000", 6) != 0){ + fsreply(fs, r, "unrecognized 9P version"); + return; + } + r->f.version = "9P2000"; + + fsreply(fs, r, nil); +} + +void +fsflush(Fs *fs, Request *r, Fid *f) +{ + Request *or; + + or = remtag(&f->r, r->f.oldtag); + if(or != nil){ + fsputfid(fs, or->fid); + free(or); + } + fsreply(fs, r, nil); +} + +void +fsattach(Fs *fs, Request *r, Fid *f) +{ + f->qid.type = QTDIR; + f->qid.path = QID(0, Ttopdir); + f->qid.vers = 0; + + if(r->f.uname[0]) + f->user = strdup(r->f.uname); + else + f->user = strdup("none"); + + /* hold down the fid till the clunk */ + f->attached = 1; + lock(fs); + f->ref++; + unlock(fs); + + r->f.qid = f->qid; + fsreply(fs, r, nil); +} + +void +fswalk(Fs *fs, Request *r, Fid *f) +{ + char *name; + Dir d; + int i, n, nqid, nwname; + Qid qid, wqid[MAXWELEM]; + Fid *nf; + char *err; + + if(f->attached == 0){ + fsreply(fs, r, Enofid); + return; + } + + nf = nil; + if(r->f.fid != r->f.newfid){ + nf = fsgetfid(fs, r->f.newfid); + nf->attached = f->attached; + nf->open = f->open; + nf->qid = f->qid; + nf->user = strdup(f->user); + nf->c = f->c; + nf->wp = nf->buf; + nf->rp = nf->wp; + f = nf; + } + + qid = f->qid; + err = nil; + nwname = r->f.nwname; + nqid = 0; + if(nwname > 0){ + for(; err == nil && nqid < nwname; nqid++){ + if(nqid >= MAXWELEM){ + err = "too many name elements"; + break; + } + name = r->f.wname[nqid]; + if(strcmp(name, "..") == 0) + qid = parentqid(qid); + else if(strcmp(name, ".") != 0){ + for(i = 0; ; i++){ + n = fsdirgen(fs, qid, i, &d, nil, 0); + if(n < 0){ + err = Eexist; + break; + } + if(n > 0 && strcmp(name, d.name) == 0){ + qid = d.qid; + break; + } + } + } + wqid[nqid] = qid; + } + if(nf != nil && nqid < nwname) + fsputfid(fs, nf); + if(nqid == nwname) + f->qid = qid; + } + + memmove(r->f.wqid, wqid, nqid*sizeof(Qid)); + r->f.nwqid = nqid; + fsreply(fs, r, err); +} + +int +ingroup(char *user, char *group) +{ + Ndbtuple *t, *nt; + Ndbs s; + + t = ndbsearch(db, &s, "group", group); + if(t == nil) + return 0; + for(nt = t; nt; nt = nt->entry){ + if(strcmp(nt->attr, "uid") == 0) + if(strcmp(nt->val, user) == 0) + break; + } + ndbfree(t); + return nt != nil; +} + +int +userok(char *u, char *cname) +{ + Ndbtuple *t, *nt; + Ndbs s; + + t = ndbsearch(db, &s, "console", cname); + if(t == nil) + return 0; + + for(nt = t; nt; nt = nt->entry){ + if(strcmp(nt->attr, "uid") == 0) + if(strcmp(nt->val, u) == 0) + break; + if(strcmp(nt->attr, "gid") == 0) + if(ingroup(u, nt->val)) + break; + } + ndbfree(t); + + return nt != nil; +} + +int m2p[] ={ + [OREAD] 4, + [OWRITE] 2, + [ORDWR] 6 +}; + +/* + * broadcast a message to all listeners + */ +void +bcastmsg(Fs *fs, Console *c, char *msg, int n) +{ + Fid *fl; + + for(fl = c->flist; fl; fl = fl->cnext){ + fromconsole(fl, msg, n); + fskick(fs, fl); + } +} + +void +fsopen(Fs *fs, Request *r, Fid *f) +{ + int mode; + Console *c; + + if(f->attached == 0){ + fsreply(fs, r, Enofid); + return; + } + + if(f->open){ + fsreply(fs, r, Eisopen); + return; + } + + mode = r->f.mode & 3; + + if((QTDIR & f->qid.type) && mode != OREAD){ + fsreply(fs, r, Eperm); + return; + } + + switch(TYPE(f->qid)){ + case Qdata: + c = fs->cons[CONS(f->qid)]; + if(!userok(f->user, c->name)){ + fsreply(fs, r, Eperm); + return; + } + f->rp = f->buf; + f->wp = f->buf; + f->c = c; + lock(c); + sprint(f->mbuf, "[%s] ", f->user); + f->bufn = strlen(f->mbuf); + f->used = 0; + f->cnext = c->flist; + c->flist = f; + bcastmembers(fs, c, "+", f); + if(c->pid == 0) + fsreopen(fs, c); + unlock(c); + break; + case Qctl: + c = fs->cons[CONS(f->qid)]; + if(!userok(f->user, c->name)){ + fsreply(fs, r, Eperm); + return; + } + f->c = c; + break; + case Qstat: + c = fs->cons[CONS(f->qid)]; + if(!userok(f->user, c->name)){ + fsreply(fs, r, Eperm); + return; + } + f->c = c; + break; + } + + f->open = 1; + r->f.iounit = messagesize-IOHDRSZ; + r->f.qid = f->qid; + fsreply(fs, r, nil); +} + +void +fscreate(Fs *fs, Request *r, Fid*) +{ + fsreply(fs, r, Eperm); +} + +void +fsread(Fs *fs, Request *r, Fid *f) +{ + uchar *p, *e; + int i, m, off; + vlong offset; + Dir d; + char sbuf[ERRMAX]; + + if(f->attached == 0){ + fsreply(fs, r, Enofid); + return; + } + + if((int)r->f.count < 0){ + fsreply(fs, r, Ebadcount); + return; + } + + if(QTDIR & f->qid.type){ + p = r->buf + IOHDRSZ; + e = p + r->f.count; + offset = r->f.offset; + off = 0; + for(i=0; p<e; i++, off+=m){ + m = fsdirgen(fs, f->qid, i, &d, p, e-p); + if(m < 0) + break; + if(m > BIT16SZ && off >= offset) + p += m; + } + r->f.data = (char*)r->buf + IOHDRSZ; + r->f.count = (char*)p - r->f.data; + } else { + switch(TYPE(f->qid)){ + case Qdata: + addreq(&f->r, r); + fskick(fs, f); + return; + case Qctl: + r->f.data = (char*)r->buf+IOHDRSZ; + r->f.count = 0; + break; + case Qstat: + if(r->f.count > sizeof(sbuf)) + r->f.count = sizeof(sbuf); + i = pread(f->c->sfd, sbuf, r->f.count, r->f.offset); + if(i < 0){ + errstr(sbuf, sizeof sbuf); + fsreply(fs, r, sbuf); + return; + } + r->f.data = sbuf; + r->f.count = i; + break; + default: + fsreply(fs, r, Eexist); + return; + } + } + fsreply(fs, r, nil); +} + +void +fswrite(Fs *fs, Request *r, Fid *f) +{ + int i, eol = 0; + + if(f->attached == 0){ + fsreply(fs, r, Enofid); + return; + } + + if((int)r->f.count < 0){ + fsreply(fs, r, Ebadcount); + return; + } + + if(QTDIR & f->qid.type){ + fsreply(fs, r, Eperm); + return; + } + + switch(TYPE(f->qid)){ + default: + fsreply(fs, r, Eperm); + return; + case Qctl: + write(f->c->cfd, r->f.data, r->f.count); + break; + case Qdata: + for(i = 0; i < r->f.count; i++){ + if(r->f.data[i] == '\n'){ + if(f->c->chat && f->used) + eol = 1; + if(f->c->cronly) + r->f.data[i] = '\r'; + } + else + f->used = 1; + } + if(f->c->chat){ + fskick(fs, f); + if(!f->used) + break; + + if(f->bufn + r->f.count > Bufsize){ + r->f.count -= (f->bufn + r->f.count) % Bufsize; + eol = 1; + } + strncat(f->mbuf, r->f.data, r->f.count); + f->bufn += r->f.count; + if(eol){ + bcastmsg(fs, f->c, f->mbuf, f->bufn); + sprint(f->mbuf, "[%s] ", f->user); + f->bufn = strlen(f->mbuf); + f->used = 0; + } + } + else + write(f->c->fd, r->f.data, r->f.count); + break; + } + fsreply(fs, r, nil); +} + +void +fsclunk(Fs *fs, Request *r, Fid *f) +{ + Fid **l, *fl; + Request *nr; + + if(f->open && TYPE(f->qid) == Qdata){ + while((nr = remreq(&f->r)) != nil){ + fsputfid(fs, f); + free(nr); + } + + lock(f->c); + for(l = &f->c->flist; *l; l = &fl->cnext){ + fl = *l; + if(fl == f){ + *l = fl->cnext; + break; + } + } + bcastmembers(fs, f->c, "-", f); + if(f->c->ondemand && f->c->flist == nil) + fsreopen(fs, f->c); + unlock(f->c); + } + fsreply(fs, r, nil); + fsputfid(fs, f); +} + +void +fsremove(Fs *fs, Request *r, Fid*) +{ + fsreply(fs, r, Eperm); +} + +void +fsstat(Fs *fs, Request *r, Fid *f) +{ + int i, n; + Qid q; + Dir d; + + q = parentqid(f->qid); + for(i = 0; ; i++){ + r->f.stat = r->buf+IOHDRSZ; + n = fsdirgen(fs, q, i, &d, r->f.stat, messagesize-IOHDRSZ); + if(n < 0){ + fsreply(fs, r, Eexist); + return; + } + r->f.nstat = n; + if(r->f.nstat > BIT16SZ && d.qid.path == f->qid.path) + break; + } + fsreply(fs, r, nil); +} + +void +fswstat(Fs *fs, Request *r, Fid*) +{ + fsreply(fs, r, Eperm); +} + +void +fsreply(Fs *fs, Request *r, char *err) +{ + int n; + uchar buf[8192+IOHDRSZ]; + + if(err){ + r->f.type = Rerror; + r->f.ename = err; + } + n = convS2M(&r->f, buf, messagesize); + if(debug) + fprint(2, "%F path %llux n=%d\n", &r->f, r->fid->qid.path, n); + fsputfid(fs, r->fid); + if(write(fs->fd, buf, n) != n) + fatal("unmounted"); + free(r); +} + +/* + * called whenever input or a read request has been received + */ +void +fskick(Fs *fs, Fid *f) +{ + Request *r; + char *p, *rp, *wp, *ep; + int i; + + lock(f); + while(f->rp != f->wp){ + r = remreq(&f->r); + if(r == nil) + break; + p = (char*)r->buf; + rp = f->rp; + wp = f->wp; + ep = &f->buf[Bufsize]; + for(i = 0; i < r->f.count && rp != wp; i++){ + *p++ = *rp++; + if(rp >= ep) + rp = f->buf; + } + f->rp = rp; + r->f.data = (char*)r->buf; + r->f.count = p - (char*)r->buf; + fsreply(fs, r, nil); + } + unlock(f); +} + +void +usage(void) +{ + fprint(2, "usage: consolefs [-d] [-m mount-point] [-c console-db]\n"); + threadexitsall("usage"); +} + +void +threadmain(int argc, char **argv) +{ + fmtinstall('F', fcallfmt); + + ARGBEGIN{ + case 'd': + debug++; + break; + case 'c': + consoledb = ARGF(); + if(consoledb == nil) + usage(); + break; + case 'm': + mntpt = ARGF(); + if(mntpt == nil) + usage(); + break; + }ARGEND; + + db = ndbopen(consoledb); + if(db == nil) + fatal("can't open %s: %r", consoledb); + + fsmount(mntpt); +} |