ref: 51d7d3eb6aee563d489f52258f6bc16d43cc50de
dir: /sys/src/cmd/ktrans/fs.c/
#include <u.h> #include <libc.h> #include <fcall.h> #include <thread.h> #include <9p.h> #include "hash.h" #include "ktrans.h" static Channel *globalkbd; static char *user; char* parsekbd(Channel *out, char *buf, int n) { char *p, *e; Msg msg; for(p = buf; p < buf+n;){ msg.code = p[0]; p++; switch(msg.code){ case 'c': case 'k': case 'K': break; default: return "malformed kbd message"; } e = utfecpy(msg.buf, msg.buf + sizeof msg.buf, p); if(e == msg.buf) return "short command"; p += e - msg.buf; p++; if(send(out, &msg) == -1) return nil; } return nil; } void kbdproc(void *a) { char *s; int fd, n; char buf[128]; s = a; fd = open(s, OREAD); if(fd < 0){ fprint(2, "could not open file %s: %r", s); chanclose(globalkbd); return; } for(;;){ n = read(fd, buf, sizeof buf); if(n < 3){ continue; } parsekbd(globalkbd, buf, n); } } Trans* spawntrans(int global) { Trans *t; t = mallocz(sizeof *t, 1); if(global) t->input = globalkbd; else t->input = chancreate(sizeof(Msg), 0); t->output = chancreate(sizeof(Msg), 0); t->dict = chancreate(sizeof(Msg), 0); t->done = chancreate(1, 0); t->lang = chancreate(sizeof(char*), 0); proccreate(keyproc, t, mainstacksize); return t; } void closetrans(Trans *t) { chanclose(t->input); chanclose(t->output); chanclose(t->dict); /* wait for threads to exit */ recv(t->done, nil); recv(t->done, nil); chanfree(t->done); chanfree(t->input); chanfree(t->output); chanfree(t->dict); free(t); } enum{ Qroot, Qkbd, Qkbdin, Qlang, }; Dir dirtab[] = { {.qid={Qroot, 0, QTDIR}, .mode=0555, .name="/"}, {.qid={Qkbd, 0, QTFILE}, .mode=0600, .name="kbd"}, {.qid={Qkbdin, 0, QTFILE}, .mode=0200, .name="kbdin"}, {.qid={Qlang, 0, QTFILE}, .mode=0600, .name="lang"}, }; static int dirgen(int n, Dir *dir, void*) { n++; if(n >= nelem(dirtab)) return -1; *dir = dirtab[n]; dir->name = estrdup9p(dir->name); dir->uid = estrdup9p(user); dir->gid = estrdup9p(user); dir->muid = estrdup9p(user); return 0; } typedef struct Aux Aux; struct Aux { Ref; Reqqueue *q; Trans *t; }; static void fsattach(Req *r) { Aux *aux; Trans *t; char *aname; /* * Each attach allocates a new "keyboard". * The global attach argument denotes to * use /dev/kbd as the source of keyboard input. * * Sessions include one translation * process, and one read queue. Since * it is common for clients to constantly be * blocked on the kbd file, we need to assign it to * it's own process so we can service other requests * in the meantime. */ aname = r->ifcall.aname; if(aname != nil && strcmp(aname, "global") == 0) t = spawntrans(1); else t = spawntrans(0); aux = mallocz(sizeof *aux, 1); aux->t = t; aux->q = reqqueuecreate(); incref(aux); r->fid->aux = aux; r->ofcall.qid = dirtab[0].qid; r->fid->qid = dirtab[0].qid; respond(r, nil); } static void fsopen(Req *r) { respond(r, nil); } static void fskbd(Req *r) { Aux *aux; Msg m; char *p; char buf[1+128], *bp; Rune rn; aux = r->fid->aux; if(recv(aux->t->output, &m) == -1){ respond(r, "closing"); return; } if(m.code != 'c'){ bp = seprint(buf, buf + sizeof buf, "%c%s", m.code, m.buf); goto Send; } p = m.buf; bp = buf; for(;bp < buf + sizeof buf;){ p += chartorune(&rn, p); if(rn == Runeerror || rn == '\0') break; bp = seprint(bp, buf + sizeof buf, "c%C", rn); bp++; } if(bp >= buf + sizeof buf){ while(*bp-- != '\0') ; bp++; } Send: r->ifcall.offset = 0; readbuf(r, buf, (bp-buf)+1); respond(r, nil); } static void fsread(Req *r) { Aux *aux; Msg m; char *p; aux = r->fid->aux; switch((uint)r->fid->qid.path){ case Qroot: dirread9p(r, dirgen, nil); respond(r, nil); break; case Qkbd: reqqueuepush(aux->q, r, fskbd); break; case Qlang: m.code = 'q'; m.buf[0] = '\0'; if(send(aux->t->input, &m) == -1){ respond(r, "closing"); break; } if(recv(aux->t->lang, &p) == -1){ respond(r, "closing"); break; } snprint(m.buf, sizeof m.buf, "%s\n", p); readstr(r, m.buf); respond(r, nil); break; default: respond(r, "bad op"); break; } } static void fswrite(Req *r) { Aux *aux; int n, lang; char *err, *p; Msg m; aux = r->fid->aux; n = r->ifcall.count; switch((uint)r->fid->qid.path){ case Qkbdin: if(n < 3){ respond(r, "short write"); return; } err = parsekbd(aux->t->input, r->ifcall.data, n); if(err != nil){ respond(r, err); return; } break; case Qlang: if(n >= sizeof m.buf){ respond(r, "large write"); return; } memmove(m.buf, r->ifcall.data, n); m.buf[n] = '\0'; p = strchr(m.buf, '\n'); if(p != nil) *p = '\0'; lang = parselang(m.buf); if(lang < 0){ respond(r, "unkonwn lang"); return; } m.buf[0] = lang; m.buf[1] = '\0'; m.code = 'c'; send(aux->t->input, &m); } r->ofcall.count = n; respond(r, nil); } static void fsstat(Req *r) { if(dirgen(r->fid->qid.path - 1, &r->d, nil) == -1) respond(r, "invalid fid"); else respond(r, nil); } static char* fswalk1(Fid *fid, char *name, Qid *qid) { int i; if(fid->qid.path != Qroot) return "walk from non root"; for(i = 0; i < nelem(dirtab); i++) if(strcmp(name, dirtab[i].name) == 0){ *qid = dirtab[i].qid; break; } if(i == nelem(dirtab)) return "file does not exist"; fid->qid = *qid; return nil; } static char* fsclone(Fid *oldfid, Fid *newfid) { Aux *aux; aux = oldfid->aux; incref(aux); newfid->aux = aux; return nil; } static void fidclunk(Fid *fid) { Aux *aux; aux = fid->aux; if(decref(aux) != 0) return; closetrans(aux->t); reqqueuefree(aux->q); } static Srv fs = { .attach=fsattach, .open=fsopen, .read=fsread, .write=fswrite, .stat=fsstat, .walk1=fswalk1, .clone=fsclone, .destroyfid=fidclunk, }; void launchfs(char *srv, char *mnt, char *kbd) { int fd; char buf[128]; user = getenv("user"); if(user == nil) user = "glenda"; if(kbd != nil){ globalkbd = chancreate(sizeof(Msg), 0); proccreate(kbdproc, kbd, mainstacksize); } fd = threadpostsrv(&fs, srv); if(fd < 0) sysfatal("postsrv %r"); if(kbd != nil){ if(mount(fd, -1, mnt, MREPL, "global") < 0) sysfatal("mount %r"); snprint(buf, sizeof buf, "%s/kbd", mnt); if(bind(buf, "/dev/kbd", MREPL) < 0) sysfatal("bind %r"); snprint(buf, sizeof buf, "%s/kbdin", mnt); if(bind(buf, "/dev/kbdin", MREPL) < 0) sysfatal("bind %r"); } else if(mount(fd, -1, mnt, MREPL, "") < 0) sysfatal("mount %r"); }