#include #include #include #include #include #include #include #include #include #include #include <9p.h> #include /* Pending: * 1. Needs a rewrite, along the lines of mousefs.c * 2. Should not send runes in the clear. */ typedef struct Dev Dev; enum { Qdir = 0, Qcons, Qctl, Qmax, Ndevs = 64, Nevs = 32, Stack = 16 * 1024, Rreclaim = L'' // rune to reclaim kbd (F1) }; struct Dev { char name; // char to stampt events char* addr; // IP for the machine servicing this int gone; int con; // to peer. Channel*evs; // of char*; sent to peer. }; int mainstacksize = Stack; static char Ebad[] = "something bad happened"; static char Eperm[] = "permission denied"; static char Enotexist[] = "file not found"; static char Eattach[] = "unknown specifier in attach"; static char Ebadctl[] = "bad control request"; static Lock devslock; static Dev* devs[Ndevs]; static int ndevs; static int debug; static Channel* kbdc; // devs[i].evs: where to send our events. static char* addr; static char* vname; static char* usr; static char* localaddr; static int doauth = 1; #define dprint if(debug)fprint #define Dprint if(debug>1)fprint static int alarmed(void *, char *s) { if(strcmp(s, "alarm") == 0) return 1; return 0; } static char* csquery(char *addr) { static char buf[128]; char* p; char* e; int fd, n; if (!addr || !strcmp(addr, "local")) return estrdup9p(localaddr); fd = open("/net/cs", ORDWR); if(fd < 0){ fprint(2, "cannot open /net/cs: %r"); return nil; } addr = netmkaddr(addr, "tcp", "11001"); if(write(fd, addr, strlen(addr)) <= 0){ fprint(2, "translating %s: %r\n", addr); close(fd); return nil; } seek(fd, 0, 0); buf[0] = 0; n = read(fd, buf, sizeof(buf)-1); close(fd); if (n < 0){ fprint(2, "cannot read /net/cs: %r"); return nil; } buf[n] = 0; p = strchr(buf, ' '); if (!p) return nil; p++; e = strchr(p, '!'); if (e){ // If it's the std service (port kbd), take just // the machine name. // For kbdfs's started at ports other than kbd, // take all the address, including the port. if (!strncmp(e+1, "11001", 5)) *e = 0; else return p ? smprint("tcp!%s", p) : nil; } return p ? estrdup9p(p) : nil; } static int islocal(char* addr) { if (!addr || !strcmp(addr, "local")) return 1; if (!strcmp(addr, sysname()) || !strcmp(addr, localaddr)) return 1; return 0; } static int _setdest(char* addr) { int i; if (islocal(addr)){ i = 0; } else { for (i = 1; i < ndevs; i++) if (devs[i]->addr != nil && !strcmp(addr, devs[i]->addr)) break; if (i == ndevs) return 0; } kbdc = devs[i]->evs; // send our events to it. dprint(2, "kbdfs: setdest %s\n", addr); return 1; } static int setdest(char* addr) { int r; lock(&devslock); r = _setdest(addr); unlock(&devslock); return r; } static void sendproc(void* a) { Dev* dev; Rune r; char s[UTFmax]; dev = a; threadsetname("sendproc"); while(recv(dev->evs, &r)>= 0 && r && !dev->gone){ Dprint(2, "kbd sent: %C\n", r); runetochar(s, &r); if (write(dev->con, s, UTFmax) != UTFmax) break; } lock(&devslock); dprint(2, "kbdfs: %s sendproc exiting\n", dev->addr); free(dev->addr); dev->addr = nil; unlock(&devslock); r = 0; runetochar(s, &r); write(dev->con, s, UTFmax); close(dev->con); dev->con = -1; if (dev->evs == kbdc) setdest("local"); threadexits(nil); } static void hangupdev(Dev* d) { Rune z; if (d == devs[0]) // local never goes return; d->gone = 1; z = 0; send(d->evs, &z); } static Dev* outgoing(void) { int i; lock(&devslock); for (i = 1; i < ndevs; i++) if (devs[i]->addr != nil && kbdc == devs[i]->evs){ unlock(&devslock); return devs[i]; } unlock(&devslock); return nil; } static int gone(char* addr) { int i; /* We skip devs[0]. Local is never gone */ lock(&devslock); for (i = 1; i < ndevs; i++) if (devs[i]->addr != nil && strstr(devs[i]->addr, addr)){ if (kbdc == devs[i]->evs){ dprint(2, "kbdfs: current gone\n"); _setdest("local"); } hangupdev(devs[i]); break; } unlock(&devslock); return i < ndevs; } static void recvproc(void* a) { char buf[UTFmax]; Rune r; Dev* d; int fd; int n; Dev* outd; d = a; threadsetname("recvproc"); fd = d->con; for(;;){ n = read(d->con, buf, UTFmax); if (n < 0 || d->gone){ dprint(2, "kbdfs: %s gone %d %d\n", d->addr, n, d->gone); break; } if (n == 0) continue; chartorune(&r, buf); if (!r){ if (d == devs[0]){ outd = outgoing(); setdest("local"); if (outd) hangupdev(outd); continue; } else { dprint(2, "kbdfs: %s end\n", d->addr); break; } } buf[n] = 0; Dprint(2, "kbd read: %C\n", r); if (d != devs[0]){ if (kbdc == d->evs){ // Got events from there. // cannot use d as the current output. gone(d->addr); } } if (kbdc != nil){ buf[1] = d->name; send(kbdc, &r); } else fprint(2, "kbdfs: no kbdc\n"); } dprint(2, "kbdfs: %s recvproc exiting\n", d->addr); if (d->evs == kbdc) setdest("local"); r = 0; if (d != devs[0]) send(d->evs, &r); d->con = -1; runetochar(buf, &r); write(fd, buf, UTFmax); close(fd); threadexits(nil); } static Dev* newdev(char* addr, int fd) { Dev* dev; int i; Rune r; if (0 && strchr(addr, '!')){ // that's ok with more than one kbdfs per machine. fprint(2, "kbdfs: newdev: bad address %s\n", addr); return nil; } if (islocal(addr)) addr = localaddr; dprint(2, "kbdfs: newdev %s\n", addr); lock(&devslock); dev = nil; for (i = 0; i < ndevs; i++){ if (devs[i]->addr == nil){ dev = devs[i]; while(nbrecv(devs[i]->evs, &r) > 0) ; break; } } if (dev == nil){ dev = emalloc9p(sizeof(Dev)); dev->evs = chancreate(sizeof(Rune), Nevs); assert(ndevs < Ndevs); devs[ndevs++] = dev; } dev->gone = 0; dev->addr = estrdup9p(addr); dev->con = fd; unlock(&devslock); return dev; } static void fsattach(Req *r) { char *spec; spec = r->ifcall.aname; if(spec != nil && spec[0] != 0){ respond(r, Eattach); return; } r->ofcall.qid = (Qid){Qdir, 0, QTDIR}; r->fid->qid = r->ofcall.qid; respond(r, nil); } static void fsopen(Req *r) { int p; r->ifcall.mode &= 3; if (r->fid->qid.type == QTAUTH || r->ifcall.mode == OREAD){ r->fid->omode = r->ifcall.mode; } else { p = r->fid->qid.path; switch(p){ case Qdir: respond(r, Eperm); return; case Qcons: case Qctl: r->fid->omode = r->ifcall.mode; break; default: respond(r, Ebad); return; } } respond(r, nil); } static int dirgen(int i, Dir *d, void*) { if(i > 1) return -1; memset(d, 0, sizeof *d); d->uid = estrdup9p("sys"); d->gid = estrdup9p("sys"); d->length = 0; switch(i){ case -1: d->qid.type = QTDIR; d->qid.path = Qdir; d->mode = 0555|DMDIR; d->name = estrdup9p("/"); break; case 0: d->mode = 0660; d->qid.path = Qcons; d->name = estrdup9p("cons"); break; case 1: d->mode = 0220; d->qid.path = Qctl; d->name = estrdup9p("kbdctl"); break; default: return -1; } return 0; } void kreadproc(void* a) { static char locked = 0; Rune ev; Req* r; Channel* kreadreq = a; char s[UTFmax+1]; int n; threadsetname("kreadproc"); for(;;){ r = recvp(kreadreq); if (recv(devs[0]->evs, &ev) < 0){ fprint(2, "kbdfs: nil event?"); break; } n = runetochar(s, &ev); r->ofcall.count = n; if (r->ifcall.count < r->ofcall.count) r->ofcall.count = r->ifcall.count; memmove(r->ofcall.data, s, r->ofcall.count); Dprint(2, "fsread: %s\n", s); respond(r, nil); } } static void fsread(Req *r) { static Channel* kreadreqc = nil; static char locked = 0; char m[200]; char* e; char* c; int p; int i; p = r->fid->qid.path; switch(p){ case Qdir: dirread9p(r, dirgen, nil); break; case Qcons: if (kreadreqc == nil){ kreadreqc = chancreate(sizeof(Req*), Nevs); proccreate(kreadproc, kreadreqc, Stack); } sendp(kreadreqc, r); return; case Qctl: e = m; lock(&devslock); for (i = 0; i < ndevs; i++) if (devs[i]->addr){ c = (kbdc == devs[i]->evs) ? "*" : ""; e = seprint(e, m+sizeof(m), "%s %s\n", devs[i]->addr, c); } unlock(&devslock); readstr(r, m); break; default: respond(r, Ebad); return; } respond(r, nil); } static int authfd(int fd, char* role) { AuthInfo*i; i = nil; alarm(5*1000); USED(i); i = auth_proxy(fd, nil, "proto=p9any user=%s role=%s", usr, role); alarm(0); if (i == nil) return 0; auth_freeAI(i); return 1; } static int remote(char* addr) { int fd; Dev* dev; if (islocal(addr)){ dprint(2, "remote for local addr\n"); return 1; } fd = dial(netmkaddr(addr, "tcp", "kbd"), 0, 0, 0); if (fd < 0){ fprint(2, "kbdfs: %s: %r\n", addr); return 0; } if (doauth && !authfd(fd, "client")){ fprint(2, "kbdfs: can't auth to %s: %r\n", addr); close(fd); return 0; } dev = newdev(addr, fd); assert(dev); proccreate(recvproc, dev, Stack); proccreate(sendproc, dev, Stack); return 1; } static char* ctl(char* msg) { char* args[4]; int nargs; char* addr; /* call address * close address */ nargs = tokenize(msg, args, nelem(args)); if (nargs != 2) return Ebadctl; addr = csquery(args[1]); if (strcmp(args[0], "call") == 0){ if (!setdest(addr)){ remote(addr); if (!setdest(addr)){ free(addr); return Enotexist; } } } else if (strcmp(args[0], "close") == 0){ if (!gone(addr)){ free(addr); return Enotexist; } } else { free(addr); return Ebadctl; } free(addr); return nil; } static void fswrite(Req *r) { static int cfd = -1; int p; char msg[80]; int n; char* e; p = r->fid->qid.path; switch(p){ case Qcons: if (cfd < 0) cfd = open("#c/cons", OWRITE|OCEXEC); n = r->ifcall.count; r->ofcall.count = write(cfd, r->ifcall.data, n); break; case Qdir: respond(r, Ebad); return; case Qctl: n = r->ofcall.count = r->ifcall.count; if (n > sizeof(msg)-1) n = sizeof(msg)-1; memmove(msg, r->ifcall.data, n); msg[n] = 0; e = ctl(msg); if (e != nil){ respond(r, e); return; } break; default: respond(r, Ebad); return; } respond(r, nil); } static char* fswalk1(Fid *fid, char *name, Qid* q) { if (fid->qid.path != Qdir){ if(strcmp(name, "..") == 0){ fid->qid.path = Qdir; fid->qid.type = QTDIR; *q = fid->qid; return nil; } return Enotexist; } if (strcmp(name, "..") == 0){ fid->qid.path = Qdir; fid->qid.type = QTDIR; } else if (strcmp(name, "cons") == 0){ fid->qid.path = Qcons; fid->qid.type = 0; } else if (strcmp(name, "kbdctl") == 0){ fid->qid.path = Qctl; fid->qid.type = 0; } else return Enotexist; *q = fid->qid; return nil; } static void fsstat(Req *r) { int q; q = r->fid->qid.path; switch(q){ case Qdir: dirgen(-1, &r->d, nil); break; case Qcons: dirgen(0, &r->d, nil); break; case Qctl: dirgen(1, &r->d, nil); break; } respond(r, nil); } static Srv mfs= { .attach=fsattach, .open= fsopen, .read= fsread, .stat= fsstat, .write= fswrite, .walk1= fswalk1, }; static void listener(void *a) { int afd, lfd; char adir[40]; char ldir[40]; char* addr = a; int dfd; NetConnInfo*i; Dev* dev; threadsetname("listener"); threadnotify(alarmed, 1); afd = announce(netmkaddr(addr, 0, "kbd"), adir); if (afd < 0) sysfatal("can't announce: %r"); for(;;){ lfd = listen(adir, ldir); if (lfd < 0) sysfatal("can't listen: %r"); dfd = accept(lfd, ldir); i = getnetconninfo(ldir, dfd); close(lfd); if (doauth && !authfd(dfd, "server")){ fprint(2, "auth failed for %s\n", i->rsys); close(dfd); } else { dev = newdev(i->rsys, dfd); dprint(2, "kbdfs: call from %s\n", dev->addr); setdest("local"); proccreate(recvproc, dev, Stack); proccreate(sendproc, dev, Stack); } freenetconninfo(i); } } void srvproc(void* a) { int* p = a; threadsetname("srvproc"); threadnotify(alarmed, 1); close(p[1]); mfs.infd = p[0]; mfs.outfd= p[0]; mfs.nopipe= 1; mfs.srvfd = -1; srv(&mfs); fprint(2, "kbdfs: srvproc exiting\n"); threadexits(nil); } void setpri(int pri) { int pid; char* s; int fd; pid = getpid(); s = smprint("/proc/%d/ctl", pid); fd = open(s, OWRITE); free(s); s = smprint("pri %d", pri); write(fd, s, strlen(s)); free(s); close(fd); } static void announceproc(void*) { int afd = -1; char* cnstr; cnstr = strchr(vname, ' '); if (cnstr) *cnstr++ = 0; for(;;){ afd = announcevol(afd, addr, vname, cnstr); sleep(10 * 1000); } } void usage(void) { fprint(2, "usage: %s [-AdD] [-m mnt] [-a addr]\n", argv0); exits("usage"); } void threadmain(int argc, char **argv) { char* mnt; int p[2]; int kbdfd; mnt = "/dev"; addr= "tcp!*!11001"; ARGBEGIN{ case 'A': doauth = 0; break; case 'D': chatty9p++; case 'd': debug++; break; case 'a': case 'n': addr = EARGF(usage()); break; case 'm': mnt = EARGF(usage()); break; case 'V': vname = EARGF(usage()); break; default: usage(); }ARGEND; if(argc!= 0) usage(); chdir("/"); localaddr=csquery(sysname()); usr = getuser(); if (usr == nil) usr = "none"; setpri(13); procrfork(listener, estrdup9p(addr), Stack, RFNAMEG|RFFDG|RFNOTEG); kbdfd = 0; // open("/dev/cons", ORDWR|OCEXEC); if (kbdfd < 0) sysfatal("can't open cons: %r"); newdev("local", kbdfd); setdest("local"); procrfork(recvproc, devs[0], Stack, RFNAMEG|RFFDG|RFNOTEG); if(pipe(p) < 0) sysfatal("pipe: %r"); procrfork(srvproc, p, Stack, RFNAMEG|RFFDG|RFNOTEG); close(p[0]); dprint(2, "kbdfs started\n"); if (mount(p[1], -1, mnt, MBEFORE, "") < 0) sysfatal("mount: %r"); dprint(2, "kbdfs mounted\n"); if (addr != nil && vname != nil) proccreate(announceproc, 0, 8*1024); threadexits(nil); }