#include #include #include #include #include #include #include #include #include #include #include <9p.h> #include typedef struct Dev Dev; enum { Qdir = 0, Qmouse, Qctl, Qmax, Ndevs = 64, Nevs = 128, Stack = 16 * 1024, Mevlen = 1+12*4, // see mouse(3). Stdxmax = 1280, Stdymax = 1024, }; struct Dev { char name; // char to stampt events char addr[50]; // IP for the machine servicing this int con; // to/from peer. int cfd; // ctl fd for con (for hanging up) Channel*evs; // of char*; got from peer Channel*out; // of char*; sent to peer int sender; // true if senderproc alive int receiver; // true if receiverproc alive }; static int newpeer(char* addr); 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[2]; static int debug; static int outm; // output device static int inm; // input device static char* kbdctl = "/dev/kbdctl"; static char* localaddr; static int doauth = 1; static char* vname; static char* addr; static Ref readers; static int mousefd = -1; #define dprint if(debug)fprint #define Dprint if(debug>1)fprint static int xmin, xmax, ymin, ymax; static Cursor whitearrow = { {0, 0}, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC, 0xFF, 0xF0, 0xFF, 0xF0, 0xFF, 0xF8, 0xFF, 0xFC, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC, 0xF3, 0xF8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, }, {0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x06, 0xC0, 0x1C, 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x38, 0xC0, 0x1C, 0xC0, 0x0E, 0xC0, 0x07, 0xCE, 0x0E, 0xDF, 0x1C, 0xD3, 0xB8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, } }; static void changecursor(int w) { static char curs[2*4+2*2*16]; static int cfd = -1; static int dummy; static int last = -1; if (w == last) return; if (cfd < 0){ Cursor* c = &whitearrow; cfd = open("/dev/cursor", OWRITE); BPLONG(curs+0*4, c->offset.x); BPLONG(curs+1*4, c->offset.y); memmove(curs+2*4, c->clr, 2*2*16); } if (cfd >=0){ if (w) // restore to default. write(cfd, curs, sizeof curs); else write(cfd, &dummy, sizeof(dummy)); } last = w; } static int alarmed(void *, char *s) { if(strcmp(s, "alarm") == 0) return 1; return 0; } static void getsizes(void) { int fd; char buf[80]; int n; char* p; char* e; fd = open("/dev/wctl", OREAD); if (fd < 0){ p = getenv("vgasize"); if (p == nil) p = strdup("1024x768x8"); strcpy(buf, p); free(p); p = strchr(buf, 'x'); *p++ = 0; xmax = strtol(buf, 0, 0); e = strchr(p, 'x'); *e = 0; ymax = strtol(p, 0, 0); xmin = ymin = 0; } else { n = read(fd, buf, sizeof(buf)-1); close(fd); buf[n] = 0; p = buf; xmin = strtol(p, &p, 0); ymin = strtol(p, &p, 0); xmax = strtol(p, &p, 0); ymax = strtol(p, &p, 0); } Dprint(2, "wsize: %d %d %d %d\n", xmin, ymin, xmax, ymax); } static int isreclaim(char* ev) { char buf[60]; int x, y, b; char* p; strecpy(buf, buf+sizeof(buf), ev); x = strtol(buf+2, &p, 0); y = strtol(p, &p, 0); b = strtol(p, &p, 0); return ( x - xmin < 10 && y - ymin < 10 && b == 1); } static int htonmouse(char* ev) { char buf[60]; int x, y, b; ulong m; double xscale, yscale; char* p; int isnop; strecpy(buf, buf+sizeof(buf), ev); x = strtol(buf+2, &p, 0); y = strtol(p, &p, 0); b = strtol(p, &p, 0); m = strtoul(p, &p, 0); isnop = (x == 0 && y == 0 && b == 0); x -= xmin; y -= ymin; xscale = (double)Stdxmax / (double)(xmax - xmin); yscale = (double)Stdymax / (double)(ymax - ymin); x = (int)(xscale * (double)x); y = (int)(yscale * (double)y); seprint(buf+2, buf+sizeof(buf), "%10d %11d %11d %11lud ", x, y, b, m); strcpy(ev, buf); return !isnop; } static void ntohmouse(char* ev) { char buf[60]; int x, y, b; ulong m; double xscale, yscale; char* p; Dprint(2, "n m: %s\n", ev); strecpy(buf, buf+sizeof(buf), ev); x = strtol(buf+2, &p, 0); y = strtol(p, &p, 0); b = strtol(p, &p, 0); m = strtoul(p, &p, 0); xscale = (double)(xmax - xmin) / Stdxmax; yscale = (double)(ymax - ymin) / Stdymax; x = (int)(xscale * (double)x); y = (int)(yscale * (double)y); x += xmin; y += ymin; seprint(buf+2, buf+sizeof(buf), "%10d %11d %11d %11lud ", x, y, b, m); strcpy(ev, buf); Dprint(2, "h m: %s\n", ev); } static void fstohmouse(char* ev) { char buf[60]; int x, y; char* p; strecpy(buf, buf+sizeof(buf), ev); x = strtol(buf+1, &p, 0); y = strtol(p, &p, 0); seprint(buf, buf+sizeof(buf), "m%11d %11d %11d %11lud ", x, y, 0, 0UL); strcpy(ev, buf); } static char* csquery(char *addr) { static char buf[128]; char* p; int fd, n; if (localaddr != nil) 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", "11000"); 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++; if (strchr(p, '!')) return smprint("tcp!%s", p); else { addr = smprint("tcp!%s!%s", sysname(), p); return csquery(addr); } } static int islocal(char* a) { char* la; if (!a || !strcmp(a, "local")) return 1; if (!strcmp(a, sysname()) || !strcmp(a, localaddr)) return 1; la = csquery(addr); if (la){ if (!strcmp(a, la)){ free(la); return 1; } free(la); } return 0; } static void kbdsetdest(char* addr) { static char* last = nil; char* c; int fd, port; char kaddr[50]; fd = open(kbdctl, OWRITE); if (fd < 0){ fprint(2, "mousefs: can't redirect kbd: %r\n"); return; } if (last && !islocal(last)){ dprint(2, "mousefs: kbd: close %s\n", last); fprint(fd, "close %s\n", last); } free(last); last = nil; strecpy(kaddr, kaddr+sizeof(kaddr), addr); c = strrchr(kaddr, '!'); if (c){ *c = 0; port = atoi(c+1) + 1; seprint(c, kaddr+sizeof(kaddr), "!%d", port); } dprint(2, "mousefs: kbd: call %s\n", kaddr); if (fprint(fd, "call %s\n", kaddr) < 0) dprint(2, "mousefs: kbd call failed: %r\n"); last = strdup(kaddr); close(fd); } static void local(void) { dprint(2, "local\n"); changecursor(0); inm = outm = 0; kbdsetdest("local"); } static void reclaim(void) { if (inm == 1 || outm == 1) sendp(devs[1].out, "bye"); local(); while(devs[1].sender || devs[1].receiver){ sleep(500); yield(); } } static void outgoing(void) { dprint(2, "outgoing\n"); changecursor(1); inm = 0; outm= 1; kbdsetdest(devs[1].addr); } static void incoming(void) { dprint(2, "incoming\n"); changecursor(0); inm = 1; outm= 0; kbdsetdest("local"); } static void sendproc(void* a) { Dev* d = a; char* e; threadsetname("sendproc"); dprint(2, "sendproc %p starting\n", d); while(e = recvp(d->out)){ if (strncmp(e, "bye", 3) == 0){ dprint(2, "sendproc %p: bye\n", d); write(d->con, "bye", 3); continue; } if (strncmp(e, "call ", 5) == 0){ write(d->con, e, strlen(e)); free(e); continue; } if (strlen(e) != Mevlen){ dprint(2, "sendproc %p: bad event: %s\n", d, e); } else if (e[0] == 'm'){ e[1] = ' '; if (d != &devs[0]) htonmouse(e); Dprint(2, "sent to %p %s\n", d, e); if (write(d->con, e, Mevlen) != Mevlen){ fprint(2, "%s: write: %r\n", argv0); free(e); write(d->con, "bye", 3); break; } } free(e); } write(d->con, "bye", 3); write(d->cfd, "hangup", 6); close(d->cfd); d->cfd = -1; d->addr[0] = 0; d->sender = 0; dprint(2, "sendproc %p exiting\n", d); threadexits(nil); } static void recvproc(void* a) { char buf[Mevlen+1]; Dev* d; int n; char* caddr; d = a; threadsetname("recvproc"); dprint(2, "recvproc %p starting\n", d); caddr = nil; for(;;){ n = read(d->con, buf, Mevlen); if (n == 0){ dprint(2, "recvproc %p: eof\n", d); break; } if (n < 0){ dprint(2, "%s: recvproc %p: %r\n", argv0, d); break; } buf[n] = 0; Dprint(2, "msread: %s\t%p i=%p o=%p\n", buf, d, &devs[inm], &devs[outm]); if (!strncmp(buf, "bye", 3)){ dprint(2, "recvproc %p: bye\n", d); break; } if (!strncmp(buf, "call ", 5)){ dprint(2, "recvproc %p: call\n", d); caddr = buf + 5; break; } if (d == &devs[0]){ if (buf[0] == 'r'){ getsizes(); } if (isreclaim(buf)){ dprint(2, "mousefs: reclaim\n"); reclaim(); continue; } Dprint(2, "mouse %p send: %s\n", d, buf); buf[1] = d->name; if (outm == 0){ if (readers.ref > 0) sendp(devs[outm].evs, estrdup9p(buf)); } else if (buf[0] == 'm') sendp(devs[outm].out, estrdup9p(buf)); } else { ntohmouse(buf); Dprint(2, "mouse %p deliver: %s\n", d, buf); buf[1] = d->name; sendp(devs[0].out, estrdup9p(buf)); if (readers.ref > 0) sendp(devs[0].evs, estrdup9p(buf)); } } sendp(d->out, nil); write(d->con, "bye", 3); write(d->cfd, "hangup", 6); close(d->cfd); close(d->con); d->con = -1; dprint(2, "recvproc %p exiting\n", d); local(); d->receiver = 0; if (caddr != nil && 0){ // not safe. Still too easy to make loops reclaim(); if (newpeer(caddr)) outgoing(); } threadexits(nil); } static void initdev(Dev* dev, char name, char* addr) { dev->evs = chancreate(sizeof(char*), Nevs); dev->out = chancreate(sizeof(char*), Nevs); dev->name = name; dev->con = -1; dev->cfd = -1; if (addr) strecpy(dev->addr, dev->addr+sizeof(dev->addr), addr); } static Dev* newdev(char* addr, int fd) { Dev* dev; if (islocal(addr)){ // allowed just once dev = &devs[0]; if (dev->con >= 0) return nil; } else dev = &devs[1]; strecpy(dev->addr, dev->addr+sizeof(dev->addr), addr); dev->con = fd; dev->cfd = -1; dev->receiver = dev->sender = 1; procrfork(recvproc, dev, Stack, RFNAMEG|RFNOTEG); procrfork(sendproc, dev, Stack, RFNAMEG|RFNOTEG); dprint(2, "mousefs: newdev %s\n", addr); 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 Qmouse: incref(&readers); 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 = Qmouse; d->name = estrdup9p("mouse"); break; case 1: d->mode = 0220; d->qid.path = Qctl; d->name = estrdup9p("mousectl"); break; default: return -1; } return 0; } void mreadproc(void* a) { static char locked = 0; char* ev; int b; Req* r; Channel* mreadreq = a; threadsetname("mreadproc"); for(;;){ r = recvp(mreadreq); /* Merging mouse streams may cause pressed buttons to * be `released' because of events that come from other * mouses. To prevent this, a mouse that is holding a * button down is locking the mouse (other ones ignored). * A gone device releases the lock as well. */ for(;;){ ev = recvp(devs[0].evs); if (ev == nil){ fprint(2, "%s: nil event?", argv0); continue; } if (strlen(ev) != Mevlen){ fprint(2,"%s: bad mouse event: [%s]\n", argv0, ev); continue; } if (!locked || ev[1] == locked) break; free(ev); } b = (ev[1+11+1+11+1+10] != '0'); if (locked && locked == ev[1] && !b){ locked = 0; } else if (!locked && b){ locked = ev[1]; } ev[1] = ' '; r->ofcall.count = strlen(ev); if (r->ifcall.count < r->ofcall.count) r->ofcall.count = r->ifcall.count; memmove(r->ofcall.data, ev, r->ofcall.count); Dprint(2, "fsread: %s\n", ev); free(ev); respond(r, nil); } } static void fsread(Req *r) { static Channel* mreadreqc = nil; static char locked = 0; char m[200]; char* e; int p; int i; p = r->fid->qid.path; switch(p){ case Qdir: dirread9p(r, dirgen, nil); break; case Qmouse: /* We can't service the read ourselves. If we block * then any request on the fs would have to wait. * This is a BAD thing because we bind MBEFORE at /dev. */ if (mreadreqc == nil){ mreadreqc = chancreate(sizeof(Req*), Nevs); proccreate(mreadproc, mreadreqc, Stack); } sendp(mreadreqc, r); return; case Qctl: e = m; lock(&devslock); for (i = 0; i < nelem(devs); i++) if (devs[i].addr[0]) e = seprint(e, m+sizeof(m), "%s\n", devs[i].addr); 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", getuser(), role); alarm(0); if (i == nil) return 0; auth_freeAI(i); return 1; } static int newpeer(char* addr) { int fd; int cfd; Dev* d; char* na; na = netmkaddr(addr, "tcp", "mouse"); if (islocal(addr) || islocal(na)){ dprint(2, "%s: newpeer: local addr: %s\n", argv0, addr); return 0; } fd = dial(na, 0, 0, &cfd); if (fd < 0){ fprint(2, "mousefs: dial %s: %r\n", addr); return 0; } if (doauth && !authfd(fd, "client")){ fprint(2, "mousefs: can't auth to %s: %r\n", addr); close(fd); close(cfd); return 0; } if (d = newdev(addr, fd)){ d->cfd = cfd; fprint(d->cfd, "keepalive 2000"); } return d != nil; } static char* ctl(char* msg) { char* args[4]; int nargs; char* addr; char call[Mevlen]; char* na; /* call address * close address */ nargs = tokenize(msg, args, nelem(args)); if (nargs != 2) return Ebadctl; if (strcmp(args[0], "call") == 0){ addr = csquery(args[1]); if (addr == nil){ reclaim(); return Enotexist; } if (inm == 0){ reclaim(); if (!newpeer(addr)){ free(addr); return Enotexist; } outgoing(); } else { na = netmkaddr(addr, "tcp", "11000"); if (!islocal(addr) && !islocal(na)){ seprint(call,call+sizeof(call),"call %s",args[1]); nbsendp(devs[inm].out, estrdup9p(call)); reclaim(); } } free(addr); } else if (strcmp(args[0], "close") == 0){ reclaim(); } else if (strcmp(args[0], "debug") == 0){ debug = atoi(args[1]); } else { return Ebadctl; } return nil; } static void fswrite(Req *r) { static int mctl = -1; int p; char msg[80]; int n; char* e; p = r->fid->qid.path; if (mctl < 0) mctl = open("#m/mousectl", OWRITE|OCEXEC); switch(p){ case Qmouse: n = r->ifcall.count; if (n > sizeof(msg)-1) n = sizeof(msg)-1; memmove(msg, r->ifcall.data, n); msg[n] = 0; r->ofcall.count = n; Dprint(2, "fswrite: %s\n", msg); fstohmouse(msg); sendp(devs[0].out, estrdup9p(msg)); if (inm != 0) sendp(devs[inm].out, estrdup9p(msg)); 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){ if (strcmp(e, Ebadctl) == 0){ if(write(mctl, r->ifcall.data, n) < 0){ respond(r, Ebadctl); return; } } else { 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, "mouse") == 0){ fid->qid.path = Qmouse; fid->qid.type = 0; } else if (strcmp(name, "mousectl") == 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 Qmouse: dirgen(0, &r->d, nil); break; case Qctl: dirgen(1, &r->d, nil); break; } respond(r, nil); } static void fsclunk(Fid* f) { if (f->omode >= 0 && f->qid.path == Qmouse) decref(&readers); } static Srv mfs= { .attach=fsattach, .open= fsopen, .read= fsread, .stat= fsstat, .write= fswrite, .walk1= fswalk1, .destroyfid= fsclunk, }; 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(addr, adir); if (afd < 0) sysfatal("can't announce: %r"); for(;;){ lfd = listen(adir, ldir); if (lfd < 0) sysfatal("can't listen: %r"); reclaim(); dfd = accept(lfd, ldir); i = getnetconninfo(ldir, dfd); if (doauth && !authfd(dfd, "server")){ fprint(2, "auth failed for %s\n", i->rsys); close(lfd); close(dfd); } else if (dev = newdev(i->rsys, dfd)){ dev->receiver = dev->sender = 1; dev->cfd = lfd; fprint(dev->cfd, "keepalive 2000"); proccreate(recvproc, dev, Stack); proccreate(sendproc, dev, Stack); dprint(2, "mousefs: call from %s\n", dev->addr); incoming(); } else { close(lfd); fprint(2, "local call ignored\n"); } freenetconninfo(i); } } 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); } void srvproc(void* a) { int* p = a; threadsetname("srvproc"); getsizes(); mousefd = open("/dev/mouse", ORDWR|OCEXEC); if (mousefd < 0) sysfatal("can't open mouse: %r"); initdev(&devs[0], 'a', localaddr); initdev(&devs[1], 'b', nil); //setpri(13); newdev(localaddr, mousefd); proccreate(listener, estrdup9p(addr), Stack); threadnotify(alarmed, 1); close(p[1]); mfs.infd = p[0]; mfs.outfd= p[0]; mfs.nopipe= 1; mfs.srvfd = -1; srv(&mfs); dprint(2, "mousefs: srvproc exiting\n"); threadexitsall(nil); } 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] [-n addr]\n", argv0); exits("usage"); } void threadmain(int argc, char **argv) { char* mnt; int p[2]; mnt = "/dev"; addr= "tcp!*!11000"; 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("/"); addr = netmkaddr(addr, "tcp", "11000"); localaddr = csquery(addr); if(pipe(p) < 0) sysfatal("pipe: %r"); procrfork(srvproc, p, Stack, RFNAMEG|RFFDG|RFNOTEG); close(p[0]); if (mount(p[1], -1, mnt, MBEFORE, "") < 0) sysfatal("mount: %r"); if (addr != nil && vname != nil) proccreate(announceproc, 0, 8*1024); threadexits(nil); }