#include <9pm/u.h> #include <9pm/libc.h> #include <9pm/keyboard.h> #include <9pm/authsrv.h> #include "dat.h" #include "fns.h" void (*consdebug)(void) = nil; void (*screenputs)(char*, int) = nil; char* sysname; Queue* kbdq; /* unprocessed console input */ Queue* lineq; /* processed console input */ Queue* kprintoq; /* console output, for /dev/kprint */ Lock kprintinuse; /* test and set whether /dev/kprint is open */ static struct { QLock lk; int raw; /* true if we shouldn't process input */ int ctl; /* number of opens to the control file */ int x; /* index into line */ char line[1024]; /* current input line */ int count; int ctlpoff; /* a place to save up characters at interrupt time before dumping them in the queue */ Lock lockputc; char istage[512]; char *iw; char *ir; char *ie; } kbd; /* .iw = kbd.istage, .ir = kbd.istage, .ie = kbd.istage + sizeof(kbd.istage), }; */ vlong fasthz; static void seedrand(void); static int readtime(ulong, char*, int); static int readbintime(char*, int); static int writetime(char*, int); static int writebintime(char*, int); /* * Print a string on the console. Convert \n to \r\n for serial * line consoles. Locking of the queues is left up to the screen * or uart code. Multi-line messages to serial consoles may get * interspersed with other messages. */ static void putstrn0(char *str, int n, int usewrite) { /* * if someone is reading /dev/kprint and the message * is from the kernel (as opposed to from conswrite), * put the message there. * if not and there's an attached bit mapped display, * put the message there. */ if(kprintoq != nil && !qisclosed(kprintoq)){ if(usewrite) qwrite(kprintoq, str, n); else qiwrite(kprintoq, str, n); }else if(screenputs != nil) screenputs(str, n); } void putstrn(char *str, int n) { putstrn0(str, n, 0); } int snprint(char *s, int n, char *fmt, ...) { va_list arg; va_start(arg, fmt); n = doprint(s, s+n, fmt, arg) - s; va_end(arg); return n; } int sprint(char *s, char *fmt, ...) { int n; va_list arg; va_start(arg, fmt); n = doprint(s, s+PRINTSIZE, fmt, arg) - s; va_end(arg); return n; } char* seprint(char *buf, char *e, char *fmt, ...) { char *out; va_list arg; va_start(arg, fmt); out = doprint(buf, e, fmt, arg); va_end(arg); return out; } int noprint; int print(char *fmt, ...) { int n; va_list arg; char buf[PRINTSIZE]; if(noprint) return -1; va_start(arg, fmt); n = doprint(buf, buf+sizeof(buf), fmt, arg) - buf; va_end(arg); putstrn(buf, n); return n; } void panic(char *fmt, ...) { int n; va_list arg; char buf[PRINTSIZE]; static int panicking; kprintoq = nil; /* don't try to write to /dev/kprint */ if(panicking) for(;;); panicking = 1; strcpy(buf, "panic: "); va_start(arg, fmt); n = doprint(buf+strlen(buf), buf+sizeof(buf)-1, fmt, arg) - buf; va_end(arg); buf[n] = '\n'; putstrn(buf, n+1); abort(); } void _assert(char *s, char *file, int line) { char buf[256]; if(file) snprint(buf, sizeof buf, "assertion failed: %s:%d: %s\n", file, line, s); else snprint(buf, sizeof buf, "assertion failed: %s\n", s); dwrite(buf); abort(); } int pprint(char *fmt, ...) { int n; Chan *c; va_list arg; char buf[2*PRINTSIZE]; if(up == nil || up->fgrp == nil) return 0; c = up->fgrp->fd[2]; if(c==0 || (c->mode!=OWRITE && c->mode!=ORDWR)) return 0; n = sprint(buf, "%s %lud: ", up->text, up->pid); va_start(arg, fmt); n = doprint(buf+n, buf+sizeof(buf), fmt, arg) - buf; va_end(arg); if(waserror()) return 0; devtab[c->type]->write(c, buf, n, c->offset); poperror(); lock(&c->lk); c->offset += n; unlock(&c->lk); return n; } static void echoscreen(char *buf, int n) { char *e, *p; char ebuf[128]; int x; p = ebuf; e = ebuf + sizeof(ebuf) - 4; while(n-- > 0){ if(p >= e){ screenputs(ebuf, p - ebuf); p = ebuf; } x = *buf++; if(x == 0x15){ *p++ = '^'; *p++ = 'U'; *p++ = '\n'; } else *p++ = x; } if(p != ebuf) screenputs(ebuf, p - ebuf); } static void echo(char *buf, int n) { if(kbdq != nil) qproduce(kbdq, buf, n); if(kbd.raw) return; if(screenputs != nil) echoscreen(buf, n); } static void _kbdputc(int ch) { int n; char buf[3]; Rune r; r = ch; n = runetochar(buf, &r); if(n != 0) echo(buf, n); } /* _kbdputc, but with compose translation */ void kbdputc(int c) { int i; static int collecting, nk; static Rune kc[5]; if(c == Kalt){ collecting = 1; nk = 0; return; } if(!collecting){ _kbdputc(c); return; } kc[nk++] = c; c = latin1(kc, nk); if(c < -1) /* need more keystrokes */ return; if(c != -1) /* valid sequence */ _kbdputc(c); else for(i=0; i= size) return 0; if(off+n > size) n = size-off; memmove(buf, tmp+off, n); return n; } int readstr(ulong off, char *buf, ulong n, char *str) { int size; size = strlen(str); if(off >= size) return 0; if(off+n > size) n = size-off; memmove(buf, str+off, n); return n; } static void consreset(void) { lineq = qopen(2*1024, 0, 0, 0); if(lineq == nil) panic("printinit"); qnoblock(lineq, 1); } static Chan* consattach(char *spec) { return devattach('c', spec); } static Walkqid* conswalk(Chan *c, Chan *nc, char **name, int nname) { return devwalk(c, nc, name,nname, consdir, nelem(consdir), devgen); } static int consstat(Chan *c, uchar *dp, int n) { return devstat(c, dp, n, consdir, nelem(consdir), devgen); } static Chan* consopen(Chan *c, int omode) { c->aux = nil; c = devopen(c, omode, consdir, nelem(consdir), devgen); switch((ulong)c->qid.path){ case Qkprint: if(!canlock(&kprintinuse)){ c->flag &= ~COPEN; error(Einuse); } if(kprintoq == nil){ kprintoq = qopen(8*1024, -1, 0, 0); if(kprintoq == nil){ c->flag &= ~COPEN; error(Enomem); } qnoblock(kprintoq, 1); }else qreopen(kprintoq); c->iounit = qiomaxatomic; break; } return c; } static void consclose(Chan *c) { switch((ulong)c->qid.path){ /* close of kprint allows other opens */ case Qkprint: if(c->flag & COPEN){ unlock(&kprintinuse); qhangup(kprintoq, nil); } break; } } static long consread(Chan *c, void *buf, long n, vlong off) { char *b; char tmp[128]; /* must be >= 6*NUMSIZE */ char *cbuf = buf; int i, k, l; vlong offset = off; if(n <= 0) return n; switch((ulong)c->qid.path){ case Qdir: return devdirread(c, buf, n, consdir, nelem(consdir), devgen); case Qcputime: k = offset; if(k >= 6*NUMSIZE) return 0; if(k+n > 6*NUMSIZE) n = 6*NUMSIZE - k; /* easiest to format in a separate buffer and copy out */ for(i=0; i<6 && NUMSIZE*itime[i]; if(i == TReal) l = machp0->ticks - l; l = TK2MS(l); readnum(0, tmp+NUMSIZE*i, NUMSIZE, l, NUMSIZE); } memmove(buf, tmp+k, n); return n; case Qkprint: return qread(kprintoq, buf, n); case Qpgrpid: return readnum((ulong)offset, buf, n, up->pgrp->pgrpid, NUMSIZE); case Qpid: return readnum((ulong)offset, buf, n, up->pid, NUMSIZE); case Qppid: return readnum((ulong)offset, buf, n, 0/*up->parentpid*/, NUMSIZE); case Qtime: return readtime((ulong)offset, buf, n); case Qbintime: return readbintime(buf, n); case Qhostowner: return readstr((ulong)offset, buf, n, eve); case Qhostdomain: return readstr((ulong)offset, buf, n, hostdomain); case Quser: return readstr((ulong)offset, buf, n, eve); case Qnull: return 0; case Qsysname: if(sysname == nil) return 0; return readstr((ulong)offset, buf, n, sysname); case Qrandom: return randomread(buf, n); case Qdrivers: b = malloc(READSTR); if(b == nil) error(Enomem); n = 0; for(i = 0; devtab[i] != nil; i++) n += snprint(b+n, READSTR-n, "#%C %s\n", devtab[i]->dc, devtab[i]->name); if(waserror()){ free(b); nexterror(); } n = readstr((ulong)offset, buf, n, b); free(b); poperror(); return n; case Qzero: memset(buf, 0, n); return n; case Qosversion: snprint(tmp, sizeof tmp, "2000"); n = readstr((ulong)offset, buf, n, tmp); return n; default: print("consread 0x%llux\n", c->qid.path); error(Egreg); } return -1; /* never reached */ } static long conswrite(Chan *c, void *va, long n, vlong off) { char buf[256]; long l, bp; char *a = va; USED(off); switch((ulong)c->qid.path){ case Qcons: /* * Can't page fault in putstrn, so copy the data locally. */ l = n; while(l > 0){ bp = l; if(bp > sizeof buf - 1) bp = sizeof buf - 1; memmove(buf, a, bp); buf[bp] = 0; putstrn0(buf, bp, 1); a += bp; l -= bp; } break; case Qhostowner: return hostownerwrite(a, n); case Qhostdomain: return hostdomainwrite(a, n); case Qnull: break; default: error(Egreg); } return n; } Dev devcons = { 'c', "cons", consreset, consattach, conswalk, consstat, consopen, devcreate, consclose, consread, devbread, conswrite, devbwrite, devremove, devwstat, }; static uvlong uvorder = 0x0001020304050607; static uchar* le2vlong(vlong *to, uchar *f) { uchar *t, *o; int i; t = (uchar*)to; o = (uchar*)&uvorder; for(i = 0; i < sizeof(vlong); i++) t[o[i]] = f[i]; return f+sizeof(vlong); } static uchar* vlong2le(uchar *t, vlong from) { uchar *f, *o; int i; f = (uchar*)&from; o = (uchar*)&uvorder; for(i = 0; i < sizeof(vlong); i++) t[i] = f[o[i]]; return t+sizeof(vlong); } static long order = 0x00010203; static uchar* le2long(long *to, uchar *f) { uchar *t, *o; int i; t = (uchar*)to; o = (uchar*)ℴ for(i = 0; i < sizeof(long); i++) t[o[i]] = f[i]; return f+sizeof(long); } static uchar* long2le(uchar *t, long from) { uchar *f, *o; int i; f = (uchar*)&from; o = (uchar*)ℴ for(i = 0; i < sizeof(long); i++) t[i] = f[o[i]]; return t+sizeof(long); } char *Ebadtimectl = "bad time control"; /* * like the old #c/time but with added info. Return * * secs nanosecs fastticks fasthz */ static int readtime(ulong off, char *buf, int n) { vlong xnsec; long sec; char str[7*NUMSIZE]; xnsec = nsec(); sec = xnsec/(uvlong)1000000000; snprint(str, sizeof(str), "%*.0lud %*.0llud %*.0llud %*.0llud ", NUMSIZE-1, sec, VLNUMSIZE-1, nsec, VLNUMSIZE-1, (uvlong)0, VLNUMSIZE-1, (uvlong)0); return readstr(off, buf, n, str); } /* * read binary time info. all numbers are little endian. * ticks and nsec are syncronized. */ static int readbintime(char *buf, int n) { int i; vlong xnsec; uvlong *b = (uvlong*)buf; i = 0; xnsec = nsec(); if(n >= 3*sizeof(uvlong)){ b[2] = 0; i += sizeof(uvlong); } if(n >= 2*sizeof(uvlong)){ b[1] = 0; i += sizeof(uvlong); } if(n >= 8){ vlong2le((uchar*)b, xnsec); i += sizeof(vlong); } return i; }