#include #include #include #include #include #include #include <9p.h> enum { Nscan= 128, Qroot= 0, Qkbd, Qkbin, Qkbmap, Qcons, Qconsctl, Nqid, Rawon= 0, Rawoff, Kbdflush, STACK = 8*1024, }; typedef struct Key Key; typedef struct Scan Scan; struct Key { int down; int c; Rune r; Rune b; }; struct Scan { int esc1; int esc2; int caps; int num; int shift; int ctl; int alt; int altgr; int leds; }; struct Qtab { char *name; int mode; int type; } qtab[Nqid] = { "/", DMDIR|0500, QTDIR, "kbd", 0600, 0, "kbin", 0200, 0, "kbmap", 0600, 0, "cons", 0600, 0, "consctl", 0600, 0, }; char Eshort[] = "read count too small"; char Ebadarg[] = "invalid argument"; char Eperm[] = "permission denied"; char Einuse[] = "file in use"; char Enonexist[] = "file does not exist"; char Ebadspec[] = "bad attach specifier"; char Ewalk[] = "walk in non directory"; char Efront[] = "the front fell off"; int scanfd; int ledsfd; int consfd; int kbdopen; int consopen; int consctlopen; int debug; Channel *keychan; /* Key */ Channel *reqchan; /* Req* */ Channel *ctlchan; /* int */ Channel *rawchan; /* Rune */ Channel *runechan; /* Rune */ Channel *linechan; /* char * */ Channel *kbdchan; /* char* */ /* * The codes at 0x79 and 0x7b are produced by the PFU Happy Hacking keyboard. * A 'standard' keyboard doesn't produce anything above 0x58. */ Rune kbtab[Nscan] = { [0x00] 0, 0x1b, '1', '2', '3', '4', '5', '6', [0x08] '7', '8', '9', '0', '-', '=', '\b', '\t', [0x10] 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', [0x18] 'o', 'p', '[', ']', '\n', Kctl, 'a', 's', [0x20] 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', [0x28] '\'', '`', Kshift, '\\', 'z', 'x', 'c', 'v', [0x30] 'b', 'n', 'm', ',', '.', '/', Kshift, '*', [0x38] Kalt, ' ', Kctl, KF|1, KF|2, KF|3, KF|4, KF|5, [0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Knum, Kscroll, '7', [0x48] '8', '9', '-', '4', '5', '6', '+', '1', [0x50] '2', '3', '0', '.', 0, 0, 0, KF|11, [0x58] KF|12, 0, 0, 0, 0, 0, 0, 0, [0x60] 0, 0, 0, 0, 0, 0, 0, 0, [0x68] 0, 0, 0, 0, 0, 0, 0, 0, [0x70] 0, 0, 0, 0, 0, 0, 0, 0, [0x78] 0, Kdown, 0, Kup, 0, 0, 0, 0, }; Rune kbtabshift[Nscan] = { [0x00] 0, 0x1b, '!', '@', '#', '$', '%', '^', [0x08] '&', '*', '(', ')', '_', '+', '\b', '\t', [0x10] 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', [0x18] 'O', 'P', '{', '}', '\n', Kctl, 'A', 'S', [0x20] 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', [0x28] '"', '~', Kshift, '|', 'Z', 'X', 'C', 'V', [0x30] 'B', 'N', 'M', '<', '>', '?', Kshift, '*', [0x38] Kalt, ' ', Kctl, KF|1, KF|2, KF|3, KF|4, KF|5, [0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Knum, Kscroll, '7', [0x48] '8', '9', '-', '4', '5', '6', '+', '1', [0x50] '2', '3', '0', '.', 0, 0, 0, KF|11, [0x58] KF|12, 0, 0, 0, 0, 0, 0, 0, [0x60] 0, 0, 0, 0, 0, 0, 0, 0, [0x68] 0, 0, 0, 0, 0, 0, 0, 0, [0x70] 0, 0, 0, 0, 0, 0, 0, 0, [0x78] 0, Kup, 0, Kup, 0, 0, 0, 0, }; Rune kbtabesc1[Nscan] = { [0x00] 0, 0, 0, 0, 0, 0, 0, 0, [0x08] 0, 0, 0, 0, 0, 0, 0, 0, [0x10] 0, 0, 0, 0, 0, 0, 0, 0, [0x18] 0, 0, 0, 0, '\n', Kctl, 0, 0, [0x20] 0, 0, 0, 0, 0, 0, 0, 0, [0x28] 0, 0, Kshift, 0, 0, 0, 0, 0, [0x30] 0, 0, 0, 0, 0, '/', 0, Kprint, [0x38] Kaltgr, 0, 0, 0, 0, 0, 0, 0, [0x40] 0, 0, 0, 0, 0, 0, Kbreak, Khome, [0x48] Kup, Kpgup, 0, Kleft, 0, Kright, 0, Kend, [0x50] Kdown, Kpgdown, Kins, Kdel, 0, 0, 0, 0, [0x58] 0, 0, 0, 0, 0, 0, 0, 0, [0x60] 0, 0, 0, 0, 0, 0, 0, 0, [0x68] 0, 0, 0, 0, 0, 0, 0, 0, [0x70] 0, 0, 0, 0, 0, 0, 0, 0, [0x78] 0, Kup, 0, 0, 0, 0, 0, 0, }; Rune kbtabaltgr[Nscan] = { [0x00] 0, 0, 0, 0, 0, 0, 0, 0, [0x08] 0, 0, 0, 0, 0, 0, 0, 0, [0x10] 0, 0, 0, 0, 0, 0, 0, 0, [0x18] 0, 0, 0, 0, '\n', Kctl, 0, 0, [0x20] 0, 0, 0, 0, 0, 0, 0, 0, [0x28] 0, 0, Kshift, 0, 0, 0, 0, 0, [0x30] 0, 0, 0, 0, 0, '/', 0, Kprint, [0x38] Kaltgr, 0, 0, 0, 0, 0, 0, 0, [0x40] 0, 0, 0, 0, 0, 0, Kbreak, Khome, [0x48] Kup, Kpgup, 0, Kleft, 0, Kright, 0, Kend, [0x50] Kdown, Kpgdown, Kins, Kdel, 0, 0, 0, 0, [0x58] 0, 0, 0, 0, 0, 0, 0, 0, [0x60] 0, 0, 0, 0, 0, 0, 0, 0, [0x68] 0, 0, 0, 0, 0, 0, 0, 0, [0x70] 0, 0, 0, 0, 0, 0, 0, 0, [0x78] 0, Kup, 0, 0, 0, 0, 0, 0, }; Rune kbtabctl[Nscan] = { [0x00] 0, '', '', '', '', '', '', '', [0x08] '', '', '', '', ' ', '', '\b', '\t', [0x10] '', '', '', '', '', '', '', '\t', [0x18] '', '', '', '', '\n', Kctl, '', '', [0x20] '', '', '', '\b', '\n', ' ', ' ', '', [0x28] '', 0, Kshift, '', '', '', '', '', [0x30] '', '', ' ', ' ', '', '', Kshift, '\n', [0x38] Kalt, 0, Kctl, '', '', '', '', '', [0x40] '', '', ' ', ' ', '', '', '', '', [0x48] '', '', ' ', '', '', '', ' ', '', [0x50] '', '', '', '', 0, 0, 0, '', [0x58] ' ', 0, 0, 0, 0, 0, 0, 0, [0x60] 0, 0, 0, 0, 0, 0, 0, 0, [0x68] 0, 0, 0, 0, 0, 0, 0, 0, [0x70] 0, 0, 0, 0, 0, 0, 0, 0, [0x78] 0, '', 0, '\b', 0, 0, 0, 0, }; void reboot(void); /* * Scan code processing */ void kbdputsc(Scan *scan, int c) { Key key; /* * e0's is the first of a 2 character sequence, e1 the first * of a 3 character sequence (on the safari) */ if(c == 0xe0){ scan->esc1 = 1; return; } else if(c == 0xe1){ scan->esc2 = 2; return; } key.down = (c & 0x80) == 0; key.c = c & 0x7f; if(key.c >= Nscan) return; if(scan->esc1) key.r = kbtabesc1[key.c]; else if(scan->shift) key.r = kbtabshift[key.c]; else if(scan->altgr) key.r = kbtabaltgr[key.c]; else if(scan->ctl) key.r = kbtabctl[key.c]; else key.r = kbtab[key.c]; switch(key.r){ case Spec|0x60: key.r = Kshift; break; case Spec|0x62: key.r = Kctl; break; case Spec|0x63: key.r = Kalt; break; } if(scan->esc1) key.b = key.r; else key.b = kbtab[key.c]; if(scan->caps && key.r<='z' && key.r>='a') key.r += 'A' - 'a'; if(scan->ctl && scan->alt && key.r == Kdel) reboot(); send(keychan, &key); if(scan->esc1) scan->esc1 = 0; else if(scan->esc2) scan->esc2--; switch(key.r){ case Kshift: scan->shift = key.down; break; case Kctl: scan->ctl = key.down; break; case Kaltgr: scan->altgr = key.down; break; case Kalt: scan->alt = key.down; break; case Knum: scan->num ^= key.down; break; case Kcaps: scan->caps ^= key.down; break; } } void setleds(Scan *scan, int leds) { char buf[8]; if(ledsfd < 0 || scan->leds == leds) return; leds &= 7; snprint(buf, sizeof(buf), "%d", leds); pwrite(ledsfd, buf, strlen(buf), 0); scan->leds = leds; } /* * Read scan codes from scanfd */ void scanproc(void *) { uchar buf[64]; Scan scan; int i, n; threadsetname("scanproc"); memset(&scan, 0, sizeof scan); while((n = read(scanfd, buf, sizeof buf)) > 0){ for(i=0; i 0; r++, n--) p += runetochar(p, r); *p = 0; return s; } /* * Read key events from keychan and produce characters to * rawchan and keystate in kbdchan. this way here is only * one global keystate even if multiple keyboards are used. */ void keyproc(void *) { Rune rb[Nscan*2+1]; int cb[Nscan]; Key key; int i, nb; char *s; threadsetname("keyproc"); nb = 0; while(recv(keychan, &key) > 0){ if(key.down && key.r) nbsend(rawchan, &key.r); rb[0] = 0; for(i=0; i 0){ x = buf + n; while(p < x && fullrune(p, x - p)){ p += chartorune(&r, p); if(r){ if(r == '\n' && cr){ cr = 0; continue; } if(cr = (r == '\r')) r = '\n'; send(runechan, &r); } } n = x - p; memmove(buf, p, n); p = buf + n; } } static int nextrune(Channel *ch, Rune *r) { while(recv(ch, r) > 0){ switch(*r){ case 0: case Kcaps: case Knum: case Kshift: case Kaltgr: /* ignore modifiers */ continue; case Kctl: case Kalt: /* composing escapes */ return 1; } return 0; } return -1; } /* * Read runes from rawchan, possibly compose special characters * and output the new runes to runechan */ void runeproc(void *) { static struct { char *ld; /* must be seen before using this conversion */ char *si; /* options for last input characters */ Rune *so; /* the corresponding Rune for each si entry */ } tab[] = { #include "latin1.h" }; Rune r, rr; int i, j; int ctl; threadsetname("runeproc"); ctl = 0; while((i = nextrune(rawchan, &r)) >= 0){ if(i == 0){ ctl = 0; Forward: send(runechan, &r); continue; } if(r == Kctl){ ctl = 1; continue; } /* * emulators like qemu and vmware use Ctrl+Alt to lock * keyboard input so dont confuse them for a compose * sequence. */ if(r != Kalt || ctl) continue; if(nextrune(rawchan, &r)) continue; if(r == 'X'){ r = 0; for(i = 0; i<4; i++){ if(nextrune(rawchan, &rr)) break; r <<= 4; if(rr >= '0' && rr <= '9') r |= (rr - '0'); else if(rr >= 'a' && rr <= 'f') r |= 10 + (rr - 'a'); else if(rr >= 'A' && rr <= 'F') r |= 10 + (rr - 'A'); else break; } if(i == 4 && r) goto Forward; } else { if(nextrune(rawchan, &rr)) continue; for(i = 0; i 0){ nr--; fprint(1, "\b"); if(r == '\b') break; } continue; case Keof: /* ^D */ done = 1; break; case '\n': done = 1; /* no break */ default: rb[nr++] = r; fprint(1, "%C", r); } } while(!done && nr < nelem(rb)); sendp(linechan, utfconv(rb, nr)); } } /* * Queue reads to cons and kbd, flushing and * relay data between 9p and rawchan / kbdchan. */ void ctlproc(void *) { struct { Req *h; Req **t; } qcons, qkbd, *q; enum { Areq, Actl, Arune, Aline, Akbd, Aend }; Alt a[Aend+1]; Req *req; Fid *fid; Rune r; char *s, *b, *p, *e; int c, n, raw; Channel *cook; threadsetname("ctlproc"); cook = chancreate(sizeof(Rune), 0); if(scanfd >= 0) proccreate(scanproc, nil, STACK); /* scanfd -> keychan */ if(consfd >= 0) proccreate(consproc, nil, STACK); /* consfd -> runechan */ threadcreate(keyproc, nil, STACK); /* keychan -> rawchan, kbdchan */ threadcreate(runeproc, nil, STACK); /* rawchan -> runechan */ threadcreate(lineproc, cook, STACK); /* cook -> linechan */ raw = 0; b = p = e = nil; qcons.h = nil; qcons.t = &qcons.h; qkbd.h = nil; qkbd.t = &qkbd.h; memset(a, 0, sizeof a); a[Areq].c = reqchan; a[Areq].v = &req; a[Areq].op = CHANRCV; a[Actl].c = ctlchan; a[Actl].v = &c; a[Actl].op = CHANRCV; a[Arune].c = runechan; a[Arune].v = &r; a[Arune].op = CHANRCV; a[Aline].c = linechan; a[Aline].v = &s; a[Aline].op = CHANRCV; a[Akbd].c = kbdchan; a[Akbd].v = &s; a[Akbd].op = CHANRCV; a[Aend].op = CHANEND; for(;;){ s = nil; if(kbdopen){ a[Arune].op = qkbd.h ? CHANRCV : CHANNOP; a[Akbd].op = qkbd.h ? CHANRCV : CHANNOP; a[Aline].op = CHANNOP; }else{ a[Arune].op = (b == nil) ? CHANRCV : CHANNOP; a[Akbd].op = CHANRCV; a[Aline].op = (b == nil) ? CHANRCV : CHANNOP; } switch(alt(a)){ case Areq: fid = req->fid; if(req->ifcall.type == Tflush){ Req **rr; fid = req->oldreq->fid; q = fid->qid.path == Qcons ? &qcons : &qkbd; for(rr = &q->h; *rr && *rr != req->oldreq; rr = &((*rr)->aux)) ; if(*rr == req->oldreq){ if((*rr = req->oldreq->aux) == nil) q->t = rr; req->oldreq->aux = nil; respond(req->oldreq, "interrupted"); } respond(req, nil); } else if(req->ifcall.type == Tread){ q = fid->qid.path == Qcons ? &qcons : &qkbd; req->aux = nil; *q->t = req; q->t = &req->aux; goto Havereq; } else respond(req, Efront); break; case Actl: switch(c){ case Rawoff: case Rawon: if(raw = (c == Rawon)){ while(s = nbrecvp(linechan)) free(s); r = '\0'; send(cook, &r); free(b); b = nil; } break; case Kbdflush: while(s = nbrecvp(kbdchan)) free(s); break; } break; case Arune: if(kbdopen){ s = emalloc9p(UTFmax+2); s[0] = 'c'; s[1+runetochar(s+1, &r)] = 0; goto Havekbd; } if(raw){ s = emalloc9p(UTFmax+1); s[runetochar(s, &r)] = 0; } else { nbsend(cook, &r); break; } /* no break */ case Aline: b = s; p = s; e = s + strlen(s); Havereq: while(b && (req = qcons.h)){ if((qcons.h = req->aux) == nil) qcons.t = &qcons.h; n = e - p; if(req->ifcall.count < n) n = req->ifcall.count; req->ofcall.count = n; memmove(req->ofcall.data, p, n); respond(req, nil); p += n; if(p >= e){ free(b); b = nil; } } break; case Akbd: Havekbd: if(req = qkbd.h){ if((qkbd.h = req->aux) == nil) qkbd.t = &qkbd.h; n = strlen(s) + 1; if(n > req->ifcall.count) respond(req, Eshort); else { req->ofcall.count = n; memmove(req->ofcall.data, s, n); respond(req, nil); } } free(s); break; } } } /* * Keyboard layout maps */ Rune* kbmapent(int t, int sc) { if(sc < 0 || sc >= Nscan) return nil; switch(t){ default: return nil; case 0: return &kbtab[sc]; case 1: return &kbtabshift[sc]; case 2: return &kbtabesc1[sc]; case 3: return &kbtabaltgr[sc]; case 4: return &kbtabctl[sc]; } } void kbmapread(Req *req) { char tmp[3*12+1]; int t, sc, off, n; Rune *rp; off = req->ifcall.offset/(sizeof(tmp)-1); t = off/Nscan; sc = off%Nscan; if(rp = kbmapent(t, sc)) sprint(tmp, "%11d %11d %11d\n", t, sc, *rp); else *tmp = 0; n = strlen(tmp); if(req->ifcall.count < n) n = req->ifcall.count; req->ofcall.count = n; memmove(req->ofcall.data, tmp, n); respond(req, nil); } void kbmapwrite(Req *req) { char line[100], *lp, *b; Rune r, *rp; int sc, t, l; Fid *f; f = req->fid; b = req->ifcall.data; l = req->ifcall.count; lp = line; if(f->aux){ strcpy(line, f->aux); lp = line+strlen(line); free(f->aux); f->aux = nil; } while(--l >= 0) { *lp++ = *b++; if(lp[-1] == '\n' || lp == &line[sizeof(line)-1]) { *lp = 0; if(*line == 0){ Badarg: respond(req, Ebadarg); return; } if(*line == '\n' || *line == '#'){ lp = line; continue; } lp = line; while(*lp == ' ' || *lp == '\t') lp++; t = strtoul(line, &lp, 0); sc = strtoul(lp, &lp, 0); while(*lp == ' ' || *lp == '\t') lp++; if((rp = kbmapent(t, sc)) == nil) goto Badarg; r = 0; if(*lp == '\'' && lp[1]) chartorune(&r, lp+1); else if(*lp == '^' && lp[1]){ chartorune(&r, lp+1); if(0x40 <= r && r < 0x60) r -= 0x40; else goto Badarg; }else if(*lp == 'M' && ('1' <= lp[1] && lp[1] <= '5')) r = 0xF900+lp[1]-'0'; else if(*lp>='0' && *lp<='9') /* includes 0x... */ r = strtoul(lp, &lp, 0); else goto Badarg; *rp = r; lp = line; } } if(lp != line){ l = lp-line; f->aux = lp = emalloc9p(l+1); memmove(lp, line, l); lp[l] = 0; } req->ofcall.count = req->ifcall.count; respond(req, nil); } /* * Filesystem */ static char* getauser(void) { static char user[64]; int fd; int n; if(*user) return user; if((fd = open("/dev/user", OREAD)) < 0) strcpy(user, "none"); else { n = read(fd, user, (sizeof user)-1); close(fd); if(n < 0) strcpy(user, "none"); else user[n] = 0; } return user; } static int fillstat(ulong qid, Dir *d) { struct Qtab *t; memset(d, 0, sizeof *d); d->uid = getauser(); d->gid = getauser(); d->muid = ""; d->qid = (Qid){qid, 0, 0}; d->atime = time(0); t = qtab + qid; d->name = t->name; d->qid.type = t->type; d->mode = t->mode; return 1; } static void fsattach(Req *r) { char *spec; spec = r->ifcall.aname; if(spec && spec[0]){ respond(r, Ebadspec); return; } r->fid->qid = (Qid){Qroot, 0, QTDIR}; r->ofcall.qid = r->fid->qid; respond(r, nil); } static void fsstat(Req *r) { fillstat((ulong)r->fid->qid.path, &r->d); r->d.name = estrdup9p(r->d.name); r->d.uid = estrdup9p(r->d.uid); r->d.gid = estrdup9p(r->d.gid); r->d.muid = estrdup9p(r->d.muid); respond(r, nil); } static char* fswalk1(Fid *fid, char *name, Qid *qid) { int i; ulong path; path = fid->qid.path; switch(path){ case Qroot: if (strcmp(name, "..") == 0) { *qid = (Qid){Qroot, 0, QTDIR}; fid->qid = *qid; return nil; } for(i = fid->qid.path; iqid = *qid; return nil; } return Enonexist; default: return Ewalk; } } static void fsopen(Req *r) { Fid *f; static int need[4] = { 4, 2, 6, 1 }; struct Qtab *t; int n; f = r->fid; t = qtab + f->qid.path; n = need[r->ifcall.mode & 3]<<6; if((n & t->mode) != n) respond(r, Eperm); else{ f->aux = nil; switch((ulong)f->qid.path){ case Qkbd: if(kbdopen){ respond(r, Einuse); return; } kbdopen++; sendul(ctlchan, Kbdflush); break; case Qcons: consopen++; break; case Qconsctl: consctlopen++; break; } respond(r, nil); } } static int readtopdir(Fid*, uchar *buf, long off, int cnt, int blen) { int i, m, n; long pos; Dir d; n = 0; pos = 0; for (i = 1; i < Nqid; i++){ fillstat(i, &d); m = convD2M(&d, &buf[n], blen-n); if(off <= pos){ if(m <= BIT16SZ || m > cnt) break; n += m; cnt -= m; } pos += m; } return n; } static void fsread(Req *r) { Fid *f; f = r->fid; switch((ulong)f->qid.path){ default: respond(r, Efront); return; case Qroot: r->ofcall.count = readtopdir(f, (void*)r->ofcall.data, r->ifcall.offset, r->ifcall.count, r->ifcall.count); break; case Qkbd: case Qcons: sendp(reqchan, r); return; case Qkbmap: kbmapread(r); return; } respond(r, nil); } static void fswrite(Req *r) { Fid *f; char *p; int n, i; f = r->fid; switch((ulong)f->qid.path){ default: respond(r, Efront); return; case Qcons: n = r->ifcall.count; if(write(1, r->ifcall.data, n) != n){ responderror(r); return; } r->ofcall.count = n; break; case Qconsctl: p = r->ifcall.data; n = r->ifcall.count; if(n >= 5 && memcmp(p, "rawon", 5) == 0) sendul(ctlchan, Rawon); else if(n >= 6 && memcmp(p, "rawoff", 6) == 0) sendul(ctlchan, Rawoff); else { respond(r, Ebadarg); return; } r->ofcall.count = n; break; case Qkbin: if(f->aux == nil){ f->aux = emalloc9p(sizeof(Scan)); memset(f->aux, 0, sizeof(Scan)); } for(i=0; iifcall.count; i++) kbdputsc((Scan*)f->aux, (uchar)r->ifcall.data[i]); r->ofcall.count = i; break; case Qkbmap: kbmapwrite(r); return; } respond(r, nil); } static void fsflush(Req *r) { switch((ulong)r->oldreq->fid->qid.path) { case Qkbd: case Qcons: sendp(reqchan, r); return; } respond(r, nil); } static void fsdestroyfid(Fid *f) { void *p; if(f->omode != -1) switch((ulong)f->qid.path){ case Qkbin: case Qkbmap: if(p = f->aux){ f->aux = nil; free(p); } break; case Qkbd: kbdopen--; break; case Qcons: consopen--; break; case Qconsctl: if(--consctlopen == 0) sendul(ctlchan, Rawoff); break; } } static void fsend(Srv*) { threadexitsall(nil); } Srv fs = { .attach= fsattach, .walk1= fswalk1, .open= fsopen, .read= fsread, .write= fswrite, .stat= fsstat, .flush= fsflush, .destroyfid= fsdestroyfid, .end= fsend, }; void reboot(void) { int fd; if(debug) return; if((fd = open("/dev/reboot", OWRITE)) < 0){ fprint(2, "can't open /dev/reboot: %r\n"); return; } fprint(fd, "reboot\n"); close(fd); } void elevate(void) { char buf[128]; Dir *d, nd; int fd; if(debug) return; snprint(buf, sizeof(buf), "/proc/%d/ctl", getpid()); if((fd = open(buf, OWRITE)) < 0){ fprint(2, "can't open %s: %r\n", buf); return; } /* get higher than normal priority */ fprint(fd, "pri 16\n"); /* always present in physical memory */ fprint(fd, "noswap\n"); /* dont let anybody kill us */ if(d = dirfstat(fd)){ nulldir(&nd); nd.mode = d->mode & ~0222; dirfwstat(fd, &nd); free(d); } close(fd); } void usage(void) { fprint(2, "usage: %s [ -dD ] [ -s srv ] [ -m mntpnt ] [ file ]\n", argv0); exits("usage"); } void threadmain(int argc, char** argv) { char *mtpt = "/dev"; char *srv = nil; consfd = -1; ARGBEGIN{ case 'd': debug++; break; case 'D': chatty9p++; break; case 's': srv = EARGF(usage()); break; case 'm': mtpt = EARGF(usage()); break; default: usage(); }ARGEND if((scanfd = open("/dev/scancode", OREAD)) < 0) fprint(2, "%s: warning: can't open /dev/scancode: %r\n", argv0); if((ledsfd = open("/dev/leds", OWRITE)) < 0) fprint(2, "%s: warning: can't open /dev/leds: %r\n", argv0); if(*argv) if((consfd = open(*argv, OREAD)) < 0) fprint(2, "%s: warning: can't open %s: %r\n", argv0, *argv); keychan = chancreate(sizeof(Key), 8); reqchan = chancreate(sizeof(Req*), 0); ctlchan = chancreate(sizeof(int), 0); rawchan = chancreate(sizeof(Rune), 16); runechan = chancreate(sizeof(Rune), 32); linechan = chancreate(sizeof(char*), 16); kbdchan = chancreate(sizeof(char*), 16); if(!(keychan && reqchan && ctlchan && rawchan && runechan && linechan && kbdchan)) sysfatal("allocating chans"); elevate(); procrfork(ctlproc, nil, STACK, RFNAMEG|RFNOTEG); threadpostmountsrv(&fs, srv, mtpt, MBEFORE); }